diff options
| author | Danny van Kooten <dannyvankooten@users.noreply.github.com> | 2020-03-18 11:49:12 +0100 | 
|---|---|---|
| committer | Danny van Kooten <dannyvankooten@users.noreply.github.com> | 2020-03-18 11:49:12 +0100 | 
| commit | 379219bbe4a7843bb32ea6dc7eca375105f2a909 (patch) | |
| tree | 7420483d64a2c085285917817e4d3c7e0f89e552 | |
| parent | f5f04edbf832aa2e219c621b9895c33dce3e47ba (diff) | |
| download | unja-379219bbe4a7843bb32ea6dc7eca375105f2a909.tar.gz unja-379219bbe4a7843bb32ea6dc7eca375105f2a909.zip | |
more preparation for supporting inheritance
| -rw-r--r-- | src/template.c | 232 | ||||
| -rw-r--r-- | src/template.h | 6 | ||||
| -rw-r--r-- | tests/test_template.c | 40 | 
3 files changed, 161 insertions, 117 deletions
| 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 diff --git a/tests/test_template.c b/tests/test_template.c index cc57ba5..d4f4e7f 100644 --- a/tests/test_template.c +++ b/tests/test_template.c @@ -5,21 +5,21 @@ START_TESTS  TEST(text_only) {      char *input = "Hello world."; -    char *output = template(input, NULL); +    char *output = template_string(input, NULL);      assert_str(output, "Hello world.");      free(output);  }  TEST(expr_number) {      char *input = "Hello {{ 5 }}."; -    char *output = template(input, NULL); +    char *output = template_string(input, NULL);      assert_str(output, "Hello 5.");      free(output);  }  TEST(expr_string) {      char *input = "Hello {{ \"world\" }}."; -    char *output = template(input, NULL); +    char *output = template_string(input, NULL);      assert_str(output, "Hello world.");      free(output);  } @@ -28,7 +28,7 @@ TEST(expr_symbol) {      char *input = "Hello {{name}}.";      struct hashmap *ctx = hashmap_new();      hashmap_insert(ctx, "name", "world"); -    char *output = template(input, ctx); +    char *output = template_string(input, ctx);      assert_str(output, "Hello world.");      hashmap_free(ctx);      free(output); @@ -50,7 +50,7 @@ TEST(expr_add) {      hashmap_insert(ctx, "name", "Danny");      for (int i=0; i < ARRAY_SIZE(tests); i++) { -        char *output = template(tests[i].input, ctx); +        char *output = template_string(tests[i].input, ctx);          assert_str(output, tests[i].expected_output);          free(output);      } @@ -60,45 +60,45 @@ TEST(expr_add) {  TEST(expr_subtract) {      char *input = "Hello {{ 5 - 5 }}."; -    char *output = template(input, NULL); +    char *output = template_string(input, NULL);      assert_str(output, "Hello 0.");      free(output);  }  TEST(expr_divide) {      char *input = "Hello {{ 5 / 5 }}."; -    char *output = template(input, NULL); +    char *output = template_string(input, NULL);      assert_str(output, "Hello 1.");      free(output);  }  TEST(expr_multiply) {      char *input = "Hello {{ 5 * 5 }}."; -    char *output = template(input, NULL); +    char *output = template_string(input, NULL);      assert_str(output, "Hello 25.");      free(output);  }  TEST(expr_gt) {      char *input = "Hello {{ 5 > 4 }}."; -    char *output = template(input, NULL); +    char *output = template_string(input, NULL);      assert_str(output, "Hello 1.");      free(output);      input = "Hello {{ 5 > 6 }}."; -    output = template(input, NULL); +    output = template_string(input, NULL);      assert_str(output, "Hello 0.");      free(output);  }  TEST(expr_lt) {      char *input = "Hello {{ 5 < 4 }}."; -    char *output = template(input, NULL); +    char *output = template_string(input, NULL);      assert_str(output, "Hello 0.");      free(output);      input = "Hello {{ 4 < 5 }}."; -    output = template(input, NULL); +    output = template_string(input, NULL);      assert_str(output, "Hello 1.");      free(output);  } @@ -107,7 +107,7 @@ TEST(expr_whitespace) {      char *input = "Hello \n{{-name -}}\n.";      struct hashmap *ctx = hashmap_new();      hashmap_insert(ctx, "name", "world"); -    char *output = template(input, ctx); +    char *output = template_string(input, ctx);      assert_str(output, "Helloworld.");      hashmap_free(ctx);      free(output); @@ -123,7 +123,7 @@ TEST(for_block) {      vector_push(names, "Eric");      hashmap_insert(ctx, "names", names); -    char *output = template(input, ctx); +    char *output = template_string(input, ctx);      assert_str(output, "John, Sally, Eric, ");      vector_free(names);      hashmap_free(ctx); @@ -138,7 +138,7 @@ TEST(var_dot_notation) {      struct hashmap *ctx = hashmap_new();      hashmap_insert(ctx, "user", user); -    char *output = template(input, ctx); +    char *output = template_string(input, ctx);      assert_str(output, "Hello Danny!");      hashmap_free(ctx);      free(output); @@ -146,7 +146,7 @@ TEST(var_dot_notation) {  TEST(comments) {      char *input = "Hello {# comment here #}world."; -    char *output = template(input, NULL); +    char *output = template_string(input, NULL);      assert_str(output, "Hello world.");      free(output);  } @@ -168,7 +168,7 @@ TEST(if_block) {      hashmap_insert(ctx, "name", "Danny");      hashmap_insert(ctx, "age", "29");      for (int i=0; i < ARRAY_SIZE(tests); i++) { -        char *output = template(tests[i].input, ctx); +        char *output = template_string(tests[i].input, ctx);          assert_str(output, tests[i].expected_output);          free(output);      } @@ -193,7 +193,7 @@ TEST(if_else_block) {      hashmap_insert(ctx, "name", "Danny");      hashmap_insert(ctx, "age", "29");      for (int i=0; i < ARRAY_SIZE(tests); i++) { -        char *output = template(tests[i].input, ctx); +        char *output = template_string(tests[i].input, ctx);          assert_str(output, tests[i].expected_output);          free(output);      } @@ -209,7 +209,7 @@ TEST(buffer_alloc) {      char *text = "Lorem ipsum dolor sit amet.";      hashmap_insert(ctx, "n", text); -    char *output = template(input, ctx); +    char *output = template_string(input, ctx);      assert_str(output, text);      hashmap_free(ctx);      free(output); @@ -217,7 +217,7 @@ TEST(buffer_alloc) {  TEST(directory) {      struct env *env = env_new("./tests/data/01/"); -    char *output = render(env, "child.tmpl", NULL); +    char *output = template(env, "child.tmpl", NULL);      assert_str(output, "Header\n\nChild content\n\nFooter");      free(output);      env_free(env); | 
