From a13b511a582e0414476b810d2a0096dae088ee78 Mon Sep 17 00:00:00 2001 From: Danny van Kooten Date: Wed, 18 Mar 2020 14:55:59 +0100 Subject: inheritance working for depth 1 --- src/template.c | 111 ++++++++++++++++++++++++++++++++------------------ tests/test_template.c | 6 +-- 2 files changed, 73 insertions(+), 44 deletions(-) diff --git a/src/template.c b/src/template.c index d3066c3..cb2ad7b 100644 --- a/src/template.c +++ b/src/template.c @@ -61,6 +61,7 @@ mpc_parser_t *parser_init() { return template; } + mpc_parser_t *spaces = mpc_new("spaces"); mpc_parser_t *symbol = mpc_new("symbol"); mpc_parser_t *number = mpc_new("number"); mpc_parser_t *string = mpc_new("string"); @@ -78,19 +79,21 @@ mpc_parser_t *parser_init() { mpc_parser_t *statement_extends = mpc_new("extends"); mpc_parser_t *body = mpc_new("body"); mpc_parser_t *content = mpc_new("content"); + template = mpc_new("template"); - mpca_lang(MPCA_LANG_DEFAULT, + mpca_lang(MPCA_LANG_WHITESPACE_SENSITIVE, + " spaces : (' ')*;" " symbol : /[a-zA-Z][a-zA-Z0-9_.]*/ ;" " number : /[0-9]+/ ;" + " text : /[^{][^{%#]*/;" " string : '\"' /([^\"])*/ '\"' ;" " op : '+' | '-' | '*' | '/' | '>' | '<';" - " text : /[^{][^{%#]*/ ;" - " expression: ( | | ) ( ( | | ))* ;" - " print : \"{{\" /-? */ / *-?/ \"}}\" ;" + " expression: ( | | ) ( ( | | ))* ;" + " print : (/{{2}-? */) / *-?}}/ ;" " comment : \"{#\" /[^#][^#}]*/ \"#}\" ;" - " statement_open: \"{%\" /-? */;" - " statement_close: / *-?/ \"%}\";" - " for : \"for \" \"in\" \"endfor\" ;" + " statement_open: /{\%-? */;" + " statement_close: / *-?\%}/;" + " for : \"for \" \" in \" \"endfor\" ;" " block : \"block \" \"endblock\" ;" " extends : \"extends \" ;" " if : \"if \" ( \"else\" )? \"endif\" ;" @@ -98,13 +101,14 @@ mpc_parser_t *parser_init() { " content : | | | ;" " body : * ;" " template : /^/ /$/ ;", + spaces, symbol, op, + text, number, string, print, expression, - text, comment, statement_open, statement_close, @@ -115,8 +119,8 @@ mpc_parser_t *parser_init() { statement_extends, content, body, - template, - NULL); + template); + return template; } @@ -142,6 +146,8 @@ struct hashmap *find_blocks_in_ast(mpc_ast_t *node, struct hashmap *map) { for (int i=0; i < node->children_num; i++) { find_blocks_in_ast(node->children[i], map); } + + return map; } struct env *env_new(char *dirname) { @@ -298,15 +304,21 @@ int object_is_truthy(struct unja_object *obj) { return 0; } -struct unja_object *eval_expression_value(mpc_ast_t* node, struct hashmap *ctx) { +struct context { + struct hashmap *vars; + struct env *env; + struct template *current_template; +}; + +struct unja_object *eval_expression_value(mpc_ast_t* node, struct context *ctx) { if (strstr(node->tag, "symbol|")) { - /* Return empty string if no context was passed. Should probably signal error here. */ - if (ctx == NULL) { + /* Return empty string if no vars were passed. Should probably signal error here. */ + if (ctx->vars == NULL) { return &null_object; } char *key = node->contents; - char *value = hashmap_resolve(ctx, key); + char *value = hashmap_resolve(ctx->vars, key); /* TODO: Handle unexisting symbols (returns NULL currently) */ if (value == NULL) { @@ -323,11 +335,11 @@ struct unja_object *eval_expression_value(mpc_ast_t* node, struct hashmap *ctx) } -struct unja_object *eval_expression(mpc_ast_t* expr, struct hashmap *ctx) { - if (expr->children_num >= 2 && strstr(expr->children[1]->tag, "op")) { +struct unja_object *eval_expression(mpc_ast_t* expr, struct context *ctx) { + if (expr->children_num >= 4 && strstr(expr->children[2]->tag, "op")) { mpc_ast_t *left_node = expr->children[0]; - mpc_ast_t *op = expr->children[1]; - mpc_ast_t *right_node = expr->children[2]; + mpc_ast_t *op = expr->children[2]; + mpc_ast_t *right_node = expr->children[4]; struct unja_object *left = eval_expression_value(left_node, ctx); struct unja_object *right = eval_expression_value(right_node, ctx); @@ -360,36 +372,40 @@ struct unja_object *eval_expression(mpc_ast_t* expr, struct hashmap *ctx) { return eval_expression_value(left_node, ctx); } -int eval(struct buffer *buf, mpc_ast_t* t, struct hashmap *ctx) { +int eval(struct buffer *buf, mpc_ast_t* t, struct context *ctx) { static int trim_whitespace = 0; - - if (strstr(t->tag, "content|statement|extends")) { - char *template_name = t->children[2]->children[1]->contents; - // TODO: Render given template instead - // but replace blocks with blocks in current template - return 0; + // maybe eat whitespace going backward + if (t->children_num > 0 && strstr(t->children[0]->contents, "{{-")) { + buf->string = trim_trailing_whitespace(buf->string); } - if (strstr(t->tag, "content|statement|block")) { char *block_name = t->children[2]->contents; + + // find block in "lowest" template + struct template *templ = ctx->current_template; + mpc_ast_t *block = hashmap_get(templ->blocks, block_name); + if (block) { + return eval(buf, block->children[4], ctx); + } + + /* TODO: Keep looking for block in parent templates */ + + // just render this block if it wasn't found in any of the lower templates return eval(buf, t->children[4], ctx); } // eval print statement if (strstr(t->tag, "content|print")) { - // maybe eat whitespace going backward - if (strstr(t->children[1]->contents, "-")) { - buf->string = trim_trailing_whitespace(buf->string); - } + /* set flag for next eval() to trim leading whitespace from text */ - if (strstr(t->children[3]->contents, "-")) { + if (strstr(t->children[2]->contents, "-}}")) { trim_whitespace = 1; } - mpc_ast_t *expr = t->children[2]; + mpc_ast_t *expr = t->children[1]; struct unja_object *obj = eval_expression(expr, ctx); eval_object(buf, obj); object_free(obj); @@ -399,9 +415,9 @@ int eval(struct buffer *buf, mpc_ast_t* t, struct hashmap *ctx) { if (strstr(t->tag, "content|statement|for")) { char *tmp_key = t->children[2]->contents; char *iterator_key = t->children[4]->contents; - struct vector *list = hashmap_resolve(ctx, iterator_key); + struct vector *list = hashmap_resolve(ctx->vars, iterator_key); for (int i=0; i < list->size; i++) { - hashmap_insert(ctx, tmp_key, list->values[i]); + hashmap_insert(ctx->vars, tmp_key, list->values[i]); eval(buf, t->children[6], ctx); } return 0; @@ -442,7 +458,8 @@ int eval(struct buffer *buf, mpc_ast_t* t, struct hashmap *ctx) { return 0; } -char *render_ast(mpc_ast_t *ast, struct hashmap *ctx) { + +char *render_ast(mpc_ast_t *ast, struct context *ctx) { #if DEBUG printf("AST: \n"); mpc_ast_print(ast); @@ -458,22 +475,36 @@ char *render_ast(mpc_ast_t *ast, struct hashmap *ctx) { return buf.string; } -char *template_string(char *tmpl, struct hashmap *ctx) { +char *template_string(char *tmpl, struct hashmap *vars) { #if DEBUG printf("Template: %s\n", tmpl); #endif - mpc_ast_t *ast = parse(tmpl); - char *output = render_ast(ast, ctx); + mpc_ast_t *ast = parse(tmpl); + struct context ctx; + ctx.vars = vars; + ctx.env = NULL; + ctx.current_template = NULL; + char *output = render_ast(ast, &ctx); mpc_ast_delete(ast); return output; } -char *template(struct env *env, char *template_name, struct hashmap *ctx) { +char *template(struct env *env, char *template_name, struct hashmap *vars) { struct template *t = hashmap_get(env->templates, template_name); #if DEBUG printf("Template name: %s\n", t->name); printf("Parent: %s\n", t->parent ? t->parent : "None"); #endif - return render_ast(t->ast, ctx); + struct context ctx; + ctx.vars = vars; + ctx.env = env; + ctx.current_template = t; + + // find root template + while (t->parent != NULL) { + t = hashmap_get(env->templates, t->parent); + } + + return render_ast(t->ast, &ctx); } \ No newline at end of file diff --git a/tests/test_template.c b/tests/test_template.c index d4f4e7f..92f179c 100644 --- a/tests/test_template.c +++ b/tests/test_template.c @@ -215,14 +215,12 @@ TEST(buffer_alloc) { free(output); } -TEST(directory) { +TEST(inheritance) { struct env *env = env_new("./tests/data/01/"); char *output = template(env, "child.tmpl", NULL); - assert_str(output, "Header\n\nChild content\n\nFooter"); + assert_str(output, "Header\n\n\nChild content\n\n\n\nFooter\n"); free(output); env_free(env); } - - END_TESTS \ No newline at end of file -- cgit v1.2.3