From 379219bbe4a7843bb32ea6dc7eca375105f2a909 Mon Sep 17 00:00:00 2001
From: Danny van Kooten <dannyvankooten@users.noreply.github.com>
Date: Wed, 18 Mar 2020 11:49:12 +0100
Subject: more preparation for supporting inheritance

---
 src/template.c | 232 ++++++++++++++++++++++++++++++++++-----------------------
 src/template.h |   6 +-
 2 files changed, 141 insertions(+), 97 deletions(-)

(limited to 'src')

diff --git a/src/template.c b/src/template.c
index c43827a..d3066c3 100644
--- a/src/template.c
+++ b/src/template.c
@@ -32,6 +32,13 @@ struct env {
     struct hashmap *templates;
 };
 
+struct template {
+    char *name;
+    mpc_ast_t *ast;
+    struct hashmap *blocks;
+    char *parent;
+};
+
 /* ensure buffer has room for a string sized l, grows buffer capacity if needed */
 void buffer_reserve(struct buffer *buf, int l) {
     int req_size = buf->size + l;
@@ -47,6 +54,96 @@ void buffer_reserve(struct buffer *buf, int l) {
     }
 }
 
+
+mpc_parser_t *parser_init() {
+    static mpc_parser_t *template;
+    if (template != NULL) {
+        return template;
+    }
+
+    mpc_parser_t *symbol = mpc_new("symbol");
+    mpc_parser_t *number = mpc_new("number");
+    mpc_parser_t *string = mpc_new("string");
+    mpc_parser_t *op = mpc_new("op");
+    mpc_parser_t *text = mpc_new("text");
+    mpc_parser_t *print = mpc_new("print");
+    mpc_parser_t *expression = mpc_new("expression");
+    mpc_parser_t *comment = mpc_new("comment");
+    mpc_parser_t *statement = mpc_new("statement");
+    mpc_parser_t *statement_open = mpc_new("statement_open");
+    mpc_parser_t *statement_close = mpc_new("statement_close");
+    mpc_parser_t *statement_for = mpc_new("for");
+    mpc_parser_t *statement_if = mpc_new("if");
+    mpc_parser_t *statement_block = mpc_new("block");
+    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,
+        " symbol    : /[a-zA-Z][a-zA-Z0-9_.]*/ ;"
+        " number    : /[0-9]+/ ;"
+        " string    : '\"' /([^\"])*/ '\"' ;"
+        " op        : '+' | '-' | '*' | '/' | '>' | '<';"
+        " text      : /[^{][^{%#]*/ ;"
+        " expression: (<symbol> | <number> | <string>) (<op> (<symbol> | <number> | <string>))* ;"
+        " print     : \"{{\" /-? */ <expression> / *-?/ \"}}\" ;"
+        " comment : \"{#\" /[^#][^#}]*/ \"#}\" ;"
+        " statement_open: \"{%\" /-? */;"
+        " statement_close: / *-?/ \"%}\";"
+        " for       : <statement_open> \"for \" <symbol> \"in\" <symbol> <statement_close> <body> <statement_open> \"endfor\" <statement_close> ;"
+        " block     : <statement_open> \"block \" <symbol> <statement_close> <body> <statement_open> \"endblock\" <statement_close>;"
+        " extends   : <statement_open> \"extends \" <string> <statement_close>;"
+        " if        : <statement_open> \"if \" <expression> <statement_close> <body> (<statement_open> \"else\" <statement_close> <body>)? <statement_open> \"endif\" <statement_close> ;"
+        " statement : <for> | <block> | <extends> | <if> ;"
+        " content   : <print> | <statement> | <text> | <comment>;"
+        " body      : <content>* ;"
+        " template  : /^/ <body> /$/ ;",
+        symbol, 
+        op,
+        number,
+        string,
+        print,
+        expression, 
+        text, 
+        comment,
+        statement_open, 
+        statement_close, 
+        statement,
+        statement_if,
+        statement_for, 
+        statement_block,
+        statement_extends,
+        content, 
+        body, 
+        template, 
+        NULL);
+    return template;
+}
+
+mpc_ast_t *parse(char *tmpl) {
+    mpc_parser_t *parser = parser_init();
+    mpc_result_t r;
+
+    if (!mpc_parse("input", tmpl, parser, &r)) {
+        mpc_err_print(r.error);
+        mpc_err_delete(r.error);
+        return NULL;
+    }
+
+    return r.output;
+}
+
+struct hashmap *find_blocks_in_ast(mpc_ast_t *node, struct hashmap *map) {
+    if (strstr(node->tag, "content|statement|block")) {
+        char *name = node->children[2]->contents;
+        hashmap_insert(map, name, node);
+    }
+
+    for (int i=0; i < node->children_num; i++) {
+        find_blocks_in_ast(node->children[i], map);
+    }
+}
+
 struct env *env_new(char *dirname) {
     DIR *dr = opendir(dirname); 
     if (dr == NULL) { 
@@ -66,7 +163,19 @@ struct env *env_new(char *dirname) {
 
         char *name = de->d_name;
         char *tmpl = read_file(name);
-        hashmap_insert(env->templates, name, tmpl);
+        mpc_ast_t *ast = parse(tmpl);
+
+        struct template *t = malloc(sizeof *t);
+        t->ast = ast;
+        t->name = name;
+        t->blocks = find_blocks_in_ast(ast, hashmap_new());
+        t->parent = NULL;
+
+        if (ast->children_num > 1 && ast->children[1]->children_num > 0 && strstr(ast->children[1]->children[0]->tag, "content|statement|extends")) {
+            t->parent = ast->children[1]->children[0]->children[2]->children[1]->contents;
+        }
+
+        hashmap_insert(env->templates, name, t);
     }
   
     closedir(dr);     
@@ -326,110 +435,45 @@ int eval(struct buffer *buf, mpc_ast_t* t, struct hashmap *ctx) {
         return 0;
     }
 
-
-    if (strstr(t->tag, ">")) {
-        for (int i=0; i < t->children_num; i++) {
-            eval(buf, t->children[i], ctx);
-        }
-    }
-    
-    return 0;
-}
-
-mpc_parser_t *parser_init() {
-    static mpc_parser_t *template;
-    if (template != NULL) {
-        return template;
+    for (int i=0; i < t->children_num; i++) {
+        eval(buf, t->children[i], ctx);
     }
 
-    mpc_parser_t *symbol = mpc_new("symbol");
-    mpc_parser_t *number = mpc_new("number");
-    mpc_parser_t *string = mpc_new("string");
-    mpc_parser_t *op = mpc_new("op");
-    mpc_parser_t *text = mpc_new("text");
-    mpc_parser_t *print = mpc_new("print");
-    mpc_parser_t *expression = mpc_new("expression");
-    mpc_parser_t *comment = mpc_new("comment");
-    mpc_parser_t *statement = mpc_new("statement");
-    mpc_parser_t *statement_open = mpc_new("statement_open");
-    mpc_parser_t *statement_close = mpc_new("statement_close");
-    mpc_parser_t *statement_for = mpc_new("for");
-    mpc_parser_t *statement_if = mpc_new("if");
-    mpc_parser_t *statement_block = mpc_new("block");
-    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,
-        " symbol    : /[a-zA-Z][a-zA-Z0-9_.]*/ ;"
-        " number    : /[0-9]+/ ;"
-        " string    : '\"' /([^\"])*/ '\"' ;"
-        " op        : '+' | '-' | '*' | '/' | '>' | '<';"
-        " text      : /[^{][^{%#]*/ ;"
-        " expression: (<symbol> | <number> | <string>) (<op> (<symbol> | <number> | <string>))* ;"
-        " print     : \"{{\" /-? */ <expression> / *-?/ \"}}\" ;"
-        " comment : \"{#\" /[^#][^#}]*/ \"#}\" ;"
-        " statement_open: \"{%\" /-? */;"
-        " statement_close: / *-?/ \"%}\";"
-        " for       : <statement_open> \"for \" <symbol> \"in\" <symbol> <statement_close> <body> <statement_open> \"endfor\" <statement_close> ;"
-        " block     : <statement_open> \"block \" <symbol> <statement_close> <body> <statement_open> \"endblock\" <statement_close>;"
-        " extends   : <statement_open> \"extends \" <string> <statement_close>;"
-        " if        : <statement_open> \"if \" <expression> <statement_close> <body> (<statement_open> \"else\" <statement_close> <body>)? <statement_open> \"endif\" <statement_close> ;"
-        " statement : <for> | <block> | <extends> | <if> ;"
-        " content   : <print> | <statement> | <text> | <comment>;"
-        " body      : <content>* ;"
-        " template  : /^/ <body> /$/ ;",
-        symbol, 
-        op,
-        number,
-        string,
-        print,
-        expression, 
-        text, 
-        comment,
-        statement_open, 
-        statement_close, 
-        statement,
-        statement_if,
-        statement_for, 
-        statement_block,
-        statement_extends,
-        content, 
-        body, 
-        template, 
-        NULL);
-    return template;
+    return 0;
 }
 
-char *template(char *tmpl, struct hashmap *ctx) {
-    mpc_parser_t *parser = parser_init();
-    mpc_result_t r;
-
-    if (!mpc_parse("input", tmpl, parser, &r)) {
-        mpc_err_print(r.error);
-        mpc_err_delete(r.error);
-        return NULL;
-    }
-
+char *render_ast(mpc_ast_t *ast, struct hashmap *ctx) {
     #if DEBUG
-    printf("Template: %s\n", tmpl);
-    printf("AST: ");
-    mpc_ast_print(r.output);
+    printf("AST: \n");
+    mpc_ast_print(ast);
     printf("\n");
     #endif
-        
+
     struct buffer buf;
     buf.size = 0;
-    buf.cap = strlen(tmpl) + 1;
-    buf.string = malloc(buf.size);
+    buf.cap = 256;
+    buf.string = malloc(buf.cap);
     buf.string[0] = '\0';
-    eval(&buf, r.output, ctx);
-
-    mpc_ast_delete(r.output);
+    eval(&buf, ast, ctx);
     return buf.string;
 }
 
-char *render(struct env *env, char *template_name, struct hashmap *ctx) {
-    char *tmpl = hashmap_get(env->templates, template_name);
-    return template(tmpl, ctx);
+char *template_string(char *tmpl, struct hashmap *ctx) {
+    #if DEBUG
+    printf("Template: %s\n", tmpl);
+    #endif
+    mpc_ast_t *ast = parse(tmpl);        
+    char *output = render_ast(ast, ctx);
+    mpc_ast_delete(ast);
+    return output;
+}
+
+char *template(struct env *env, char *template_name, struct hashmap *ctx) {
+    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);
 }
\ No newline at end of file
diff --git a/src/template.h b/src/template.h
index f65da47..45a58fc 100644
--- a/src/template.h
+++ b/src/template.h
@@ -1,10 +1,10 @@
 #include "hashmap.h"
 #include "vector.h"
 
-char *read_file(char *filename);
-char *template(char *tmpl, struct hashmap *ctx);
 
 struct env;
 struct env *env_new();
 void env_free(struct env *env);
-char *render(struct env *env, char *template_name, struct hashmap *ctx);
\ No newline at end of file
+char *template(struct env *env, char *template_name, struct hashmap *ctx);
+char *template_string(char *tmpl, struct hashmap *ctx);
+char *read_file(char *filename);
\ No newline at end of file
-- 
cgit v1.2.3