Compare commits
No commits in common. "959be1026497b50b89501e39d9fd55feb21edc51" and "1e34cb8c2dfd6371cda4d4d5f3d926e3ac9ace61" have entirely different histories.
959be10264
...
1e34cb8c2d
|
@ -28,33 +28,4 @@ ltm_err ltm_template_compile(ltm_template **out, const char *template);
|
|||
ltm_err ltm_template_compile_n(ltm_template **out, const char *template,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* Represents a specific instance of a template.
|
||||
*/
|
||||
typedef struct ltm_instance ltm_instance;
|
||||
|
||||
/**
|
||||
* Create a new instance of the given template.
|
||||
*/
|
||||
ltm_err ltm_template_instantiate(ltm_instance **out,
|
||||
const ltm_template *template);
|
||||
|
||||
typedef enum ltm_instance_block_type {
|
||||
ltm_instance_block_type_buf = 0,
|
||||
} ltm_instance_block_type;
|
||||
|
||||
/**
|
||||
* Add a new variable to the template.
|
||||
*/
|
||||
ltm_err ltm_instance_block_add_var(ltm_instance *instance, const char *name,
|
||||
ltm_instance_block_type type, void *data,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* Add a new nested instance to the instance, returning a handle to the nested
|
||||
* instance.
|
||||
*/
|
||||
ltm_err ltm_instance_block_add_nested(ltm_instance **out,
|
||||
ltm_instance *instance, const char *name);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
typedef enum ltm_placeholder_type {
|
||||
ltm_placeholder_type_invalid = 0,
|
||||
ltm_placeholder_type_var,
|
||||
ltm_placeholder_type_nested_start,
|
||||
ltm_placeholder_type_nested_end,
|
||||
ltm_placeholder_type_loop_start,
|
||||
ltm_placeholder_type_loop_end,
|
||||
} ltm_placeholder_type;
|
||||
|
||||
/**
|
||||
|
@ -29,7 +29,7 @@ typedef struct ltm_placeholder {
|
|||
typedef enum ltm_template_block_type {
|
||||
ltm_template_block_type_literal = 0,
|
||||
ltm_template_block_type_var,
|
||||
ltm_template_block_type_nested,
|
||||
ltm_template_block_type_loop,
|
||||
} ltm_template_block_type;
|
||||
|
||||
/**
|
||||
|
@ -53,7 +53,7 @@ typedef struct ltm_template_block_name {
|
|||
|
||||
/**
|
||||
* Represents a compiled template. A template consists of a list of blocks, of
|
||||
* which some have names that can be indexed (e.g. variables, nested templates).
|
||||
* which some have names that can be indexed (e.g. variables, loops).
|
||||
*/
|
||||
struct ltm_template {
|
||||
struct {
|
||||
|
@ -66,47 +66,13 @@ struct ltm_template {
|
|||
} names;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate a new `ltm_template`.
|
||||
*/
|
||||
ltm_err ltm_template_init(ltm_template **out);
|
||||
|
||||
/**
|
||||
* Append a name to the template's list.
|
||||
*/
|
||||
ltm_err ltm_template_name_append(ltm_template *template, const char *s,
|
||||
size_t len, size_t index);
|
||||
|
||||
/**
|
||||
* Append a block to the template's list.
|
||||
*/
|
||||
ltm_err ltm_template_block_append(ltm_template *template,
|
||||
ltm_template_block_type type, void *data,
|
||||
size_t len);
|
||||
|
||||
typedef struct ltm_instance_block {
|
||||
ltm_instance_block_type type;
|
||||
struct {
|
||||
void *ptr;
|
||||
size_t len;
|
||||
} data;
|
||||
struct ltm_instance_block *next;
|
||||
} ltm_instance_block;
|
||||
|
||||
ltm_err ltm_instance_block_init(ltm_instance_block **out);
|
||||
|
||||
struct ltm_instance {
|
||||
const ltm_template *template;
|
||||
struct {
|
||||
ltm_instance_block *head;
|
||||
ltm_instance_block *tail;
|
||||
ltm_instance_block *current;
|
||||
} blocks;
|
||||
ltm_instance_block ***vars;
|
||||
// How many bytes of the current block have been written
|
||||
size_t written;
|
||||
};
|
||||
|
||||
ltm_err ltm_instance_init(ltm_instance **out);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
#include "ltm/template.h"
|
||||
#include "ltm/template_internal.h"
|
||||
|
||||
ltm_err ltm_instance_block_init(ltm_instance_block **out) {
|
||||
ltm_instance_block *block = calloc(1, sizeof(ltm_instance_block));
|
||||
|
||||
if (block == NULL) {
|
||||
return ltm_err_failed_alloc;
|
||||
}
|
||||
|
||||
*out = block;
|
||||
|
||||
return ltm_err_ok;
|
||||
}
|
||||
|
||||
ltm_err ltm_instance_init(ltm_instance **out) {
|
||||
ltm_instance *instance = calloc(1, sizeof(ltm_instance));
|
||||
|
||||
if (instance == NULL) {
|
||||
return ltm_err_failed_alloc;
|
||||
}
|
||||
|
||||
*out = instance;
|
||||
|
||||
return ltm_err_ok;
|
||||
}
|
||||
|
||||
ltm_err ltm_template_instantiate(ltm_instance **out,
|
||||
const ltm_template *template) {
|
||||
ltm_instance *instance;
|
||||
LTM_RES(ltm_instance_init(&instance));
|
||||
|
||||
instance->template = template;
|
||||
|
||||
ltm_instance_block ***vars =
|
||||
malloc(template->names.len * sizeof(ltm_instance_block **));
|
||||
|
||||
if (vars == NULL) {
|
||||
return ltm_err_failed_alloc;
|
||||
}
|
||||
|
||||
instance->vars = vars;
|
||||
|
||||
for (size_t block_index = 0; block_index < template->blocks.len;
|
||||
block_index++) {
|
||||
ltm_template_block *template_block = &template->blocks.arr[block_index];
|
||||
|
||||
switch (template_block->type) {
|
||||
case ltm_template_block_type_literal: {
|
||||
ltm_instance_block *block;
|
||||
LTM_RES(ltm_instance_block_init(&block));
|
||||
|
||||
block->type = ltm_instance_block_type_buf;
|
||||
block->data.ptr = template_block->data.ptr;
|
||||
block->data.len = template_block->data.len;
|
||||
|
||||
if (instance->blocks.head == NULL) {
|
||||
instance->blocks.head = block;
|
||||
instance->blocks.tail = block;
|
||||
instance->blocks.current = block;
|
||||
} else {
|
||||
instance->blocks.tail->next = block;
|
||||
instance->blocks.tail = block;
|
||||
}
|
||||
} break;
|
||||
case ltm_template_block_type_var:
|
||||
case ltm_template_block_type_nested:
|
||||
*vars = &instance->blocks.tail->next;
|
||||
vars++;
|
||||
}
|
||||
}
|
||||
|
||||
*out = instance;
|
||||
|
||||
return ltm_err_ok;
|
||||
}
|
|
@ -55,7 +55,7 @@ ltm_err ltm_template_block_append(ltm_template *template,
|
|||
block->data.ptr = data;
|
||||
block->data.len = len;
|
||||
break;
|
||||
case ltm_template_block_type_nested:
|
||||
case ltm_template_block_type_loop:
|
||||
block->data.ptr = data;
|
||||
break;
|
||||
// For the other cases, we explicitely ignore the data and len arguments
|
||||
|
|
|
@ -79,9 +79,9 @@ static bool ltm_template_next_placeholder(ltm_placeholder *ph, const char *s,
|
|||
size_t ident_len = temp - ident;
|
||||
|
||||
if (strncmp("start", ident, ident_len) == 0) {
|
||||
ph->type = ltm_placeholder_type_nested_start;
|
||||
ph->type = ltm_placeholder_type_loop_start;
|
||||
} else if (strncmp("end", ident, ident_len) == 0) {
|
||||
ph->type = ltm_placeholder_type_nested_end;
|
||||
ph->type = ltm_placeholder_type_loop_end;
|
||||
} else {
|
||||
ph->type = ltm_placeholder_type_invalid;
|
||||
}
|
||||
|
@ -95,15 +95,15 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
|
|||
LTM_RES(ltm_template_init(&template));
|
||||
|
||||
ltm_placeholder ph;
|
||||
bool in_nested = false;
|
||||
const char *nested_start = NULL, *nested_name = NULL;
|
||||
size_t nested_depth = 0, nested_name_len = 0;
|
||||
bool in_loop = false;
|
||||
const char *loop_start = NULL, *loop_name = NULL;
|
||||
size_t loop_depth = 0, loop_name_len = 0;
|
||||
|
||||
size_t cur_nested_depth = 0;
|
||||
size_t cur_loop_depth = 0;
|
||||
|
||||
while (ltm_template_next_placeholder(&ph, s, len)) {
|
||||
// Add part before placeholder as literal
|
||||
if (!in_nested && (ph.start != s)) {
|
||||
if (!in_loop && (ph.start != s)) {
|
||||
LTM_RES(ltm_template_block_append(
|
||||
template, ltm_template_block_type_literal, (void *)s, ph.start - s));
|
||||
}
|
||||
|
@ -113,45 +113,44 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
|
|||
case ltm_placeholder_type_invalid:
|
||||
return ltm_err_invalid_template;
|
||||
case ltm_placeholder_type_var:
|
||||
if (!in_nested) {
|
||||
if (!in_loop) {
|
||||
LTM_RES(ltm_template_block_append(template, ltm_template_block_type_var,
|
||||
NULL, 0));
|
||||
LTM_RES(ltm_template_name_append(template, ph.name.s, ph.name.len,
|
||||
template->blocks.len - 1));
|
||||
}
|
||||
break;
|
||||
case ltm_placeholder_type_nested_start:
|
||||
if (!in_nested) {
|
||||
nested_start = ph.end + 1;
|
||||
nested_depth = cur_nested_depth;
|
||||
nested_name = ph.name.s;
|
||||
nested_name_len = ph.name.len;
|
||||
in_nested = true;
|
||||
case ltm_placeholder_type_loop_start:
|
||||
if (!in_loop) {
|
||||
loop_start = ph.end + 1;
|
||||
loop_depth = cur_loop_depth;
|
||||
loop_name = ph.name.s;
|
||||
loop_name_len = ph.name.len;
|
||||
in_loop = true;
|
||||
}
|
||||
cur_nested_depth++;
|
||||
cur_loop_depth++;
|
||||
break;
|
||||
case ltm_placeholder_type_nested_end:
|
||||
// This means there's more nested end placeholders than nested starts
|
||||
if (cur_nested_depth == 0) {
|
||||
case ltm_placeholder_type_loop_end:
|
||||
// This means there's more loop end placeholders than loop starts
|
||||
if (cur_loop_depth == 0) {
|
||||
return ltm_err_invalid_template;
|
||||
}
|
||||
|
||||
cur_nested_depth--;
|
||||
cur_loop_depth--;
|
||||
|
||||
if (in_nested && (cur_nested_depth == nested_depth)) {
|
||||
size_t nested_len = ph.start - nested_start;
|
||||
ltm_template *nested_template;
|
||||
LTM_RES(
|
||||
ltm_template_compile_n(&nested_template, nested_start, nested_len));
|
||||
if (in_loop && (cur_loop_depth == loop_depth)) {
|
||||
size_t loop_len = ph.start - loop_start;
|
||||
ltm_template *loop_template;
|
||||
LTM_RES(ltm_template_compile_n(&loop_template, loop_start, loop_len));
|
||||
LTM_RES(ltm_template_block_append(
|
||||
template, ltm_template_block_type_nested, nested_template, 0));
|
||||
LTM_RES(ltm_template_name_append(template, nested_name, nested_name_len,
|
||||
template, ltm_template_block_type_loop, loop_template, 0));
|
||||
LTM_RES(ltm_template_name_append(template, loop_name, loop_name_len,
|
||||
template->blocks.len - 1));
|
||||
|
||||
in_nested = false;
|
||||
in_loop = false;
|
||||
}
|
||||
// We encountered a nested end without a start
|
||||
else if (!in_nested) {
|
||||
// We encountered a loop end without a start
|
||||
else if (!in_loop) {
|
||||
return ltm_err_invalid_template;
|
||||
}
|
||||
break;
|
||||
|
@ -161,8 +160,8 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
|
|||
s = ph.end + 1;
|
||||
}
|
||||
|
||||
// Unfinished nested
|
||||
if (in_nested) {
|
||||
// Unfinished loop
|
||||
if (in_loop) {
|
||||
return ltm_err_invalid_template;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ void test_single_placeholder_trailing() {
|
|||
TEST_CHECK(template->names.arr[0].index == 1);
|
||||
}
|
||||
|
||||
void test_single_nested() {
|
||||
void test_single_loop() {
|
||||
const char *s = "abc {{ l start }}some content {{ var }} {{ l end }}";
|
||||
|
||||
ltm_template *template;
|
||||
|
@ -67,22 +67,22 @@ void test_single_nested() {
|
|||
TEST_CHECK(template->blocks.arr[0].data.ptr == s);
|
||||
TEST_CHECK(template->blocks.arr[0].data.len == 4);
|
||||
|
||||
TEST_CHECK(template->blocks.arr[1].type == ltm_template_block_type_nested);
|
||||
ltm_template *nested_template = template->blocks.arr[1].data.ptr;
|
||||
TEST_CHECK(template->blocks.arr[1].type == ltm_template_block_type_loop);
|
||||
ltm_template *loop_template = template->blocks.arr[1].data.ptr;
|
||||
|
||||
TEST_CHECK(nested_template->blocks.len == 3);
|
||||
TEST_CHECK(nested_template->blocks.arr[0].type == ltm_template_block_type_literal);
|
||||
TEST_CHECK(nested_template->blocks.arr[0].data.ptr == s + 17);
|
||||
TEST_CHECK(nested_template->blocks.arr[0].data.len == 13);
|
||||
TEST_CHECK(nested_template->blocks.arr[1].type == ltm_template_block_type_var);
|
||||
TEST_CHECK(nested_template->blocks.arr[2].type == ltm_template_block_type_literal);
|
||||
TEST_CHECK(nested_template->blocks.arr[2].data.ptr == s + 39);
|
||||
TEST_CHECK(nested_template->blocks.arr[2].data.len == 1);
|
||||
TEST_CHECK(loop_template->blocks.len == 3);
|
||||
TEST_CHECK(loop_template->blocks.arr[0].type == ltm_template_block_type_literal);
|
||||
TEST_CHECK(loop_template->blocks.arr[0].data.ptr == s + 17);
|
||||
TEST_CHECK(loop_template->blocks.arr[0].data.len == 13);
|
||||
TEST_CHECK(loop_template->blocks.arr[1].type == ltm_template_block_type_var);
|
||||
TEST_CHECK(loop_template->blocks.arr[2].type == ltm_template_block_type_literal);
|
||||
TEST_CHECK(loop_template->blocks.arr[2].data.ptr == s + 39);
|
||||
TEST_CHECK(loop_template->blocks.arr[2].data.len == 1);
|
||||
|
||||
TEST_CHECK(nested_template->names.len == 1);
|
||||
TEST_CHECK(nested_template->names.arr[0].name.s == s + 33);
|
||||
TEST_CHECK(nested_template->names.arr[0].name.len == 3);
|
||||
TEST_CHECK(nested_template->names.arr[0].index == 1);
|
||||
TEST_CHECK(loop_template->names.len == 1);
|
||||
TEST_CHECK(loop_template->names.arr[0].name.s == s + 33);
|
||||
TEST_CHECK(loop_template->names.arr[0].name.len == 3);
|
||||
TEST_CHECK(loop_template->names.arr[0].index == 1);
|
||||
}
|
||||
|
||||
void test_unclosed_placeholder() {
|
||||
|
@ -95,7 +95,7 @@ void test_unclosed_placeholder() {
|
|||
TEST_CHECK(ltm_template_compile(&template, s) == ltm_err_invalid_template);
|
||||
}
|
||||
|
||||
void test_unclosed_nested() {
|
||||
void test_unclosed_loop() {
|
||||
ltm_template *template;
|
||||
|
||||
const char *s = "abc {{ var start }} {{ hello }}";
|
||||
|
@ -105,8 +105,8 @@ void test_unclosed_nested() {
|
|||
TEST_LIST = {
|
||||
{ "template single placeholder", test_single_placeholder },
|
||||
{ "template single placeholder trailing", test_single_placeholder_trailing },
|
||||
{ "template single nested", test_single_nested },
|
||||
{ "template single loop", test_single_loop },
|
||||
{ "template unclosed placeholder", test_unclosed_placeholder },
|
||||
{ "template unclosed nested", test_unclosed_nested },
|
||||
{ "template unclosed loop", test_unclosed_loop },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue