Compare commits

..

2 Commits

Author SHA1 Message Date
Jef Roosens 959be10264
feat(ltm): initial instantiate function
ci/woodpecker/push/build Pipeline was successful Details
2023-12-16 17:24:59 +01:00
Jef Roosens 6f817307cf
refactor(ltm): rename loops to nested 2023-12-16 16:29:08 +01:00
6 changed files with 194 additions and 54 deletions

View File

@ -28,4 +28,33 @@ ltm_err ltm_template_compile(ltm_template **out, const char *template);
ltm_err ltm_template_compile_n(ltm_template **out, const char *template, ltm_err ltm_template_compile_n(ltm_template **out, const char *template,
size_t len); 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 #endif

View File

@ -6,8 +6,8 @@
typedef enum ltm_placeholder_type { typedef enum ltm_placeholder_type {
ltm_placeholder_type_invalid = 0, ltm_placeholder_type_invalid = 0,
ltm_placeholder_type_var, ltm_placeholder_type_var,
ltm_placeholder_type_loop_start, ltm_placeholder_type_nested_start,
ltm_placeholder_type_loop_end, ltm_placeholder_type_nested_end,
} ltm_placeholder_type; } ltm_placeholder_type;
/** /**
@ -29,7 +29,7 @@ typedef struct ltm_placeholder {
typedef enum ltm_template_block_type { typedef enum ltm_template_block_type {
ltm_template_block_type_literal = 0, ltm_template_block_type_literal = 0,
ltm_template_block_type_var, ltm_template_block_type_var,
ltm_template_block_type_loop, ltm_template_block_type_nested,
} ltm_template_block_type; } 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 * Represents a compiled template. A template consists of a list of blocks, of
* which some have names that can be indexed (e.g. variables, loops). * which some have names that can be indexed (e.g. variables, nested templates).
*/ */
struct ltm_template { struct ltm_template {
struct { struct {
@ -66,13 +66,47 @@ struct ltm_template {
} names; } names;
}; };
/**
* Allocate a new `ltm_template`.
*/
ltm_err ltm_template_init(ltm_template **out); 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, ltm_err ltm_template_name_append(ltm_template *template, const char *s,
size_t len, size_t index); size_t len, size_t index);
/**
* Append a block to the template's list.
*/
ltm_err ltm_template_block_append(ltm_template *template, ltm_err ltm_template_block_append(ltm_template *template,
ltm_template_block_type type, void *data, ltm_template_block_type type, void *data,
size_t len); 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 #endif

View File

@ -0,0 +1,76 @@
#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;
}

View File

@ -55,7 +55,7 @@ ltm_err ltm_template_block_append(ltm_template *template,
block->data.ptr = data; block->data.ptr = data;
block->data.len = len; block->data.len = len;
break; break;
case ltm_template_block_type_loop: case ltm_template_block_type_nested:
block->data.ptr = data; block->data.ptr = data;
break; break;
// For the other cases, we explicitely ignore the data and len arguments // For the other cases, we explicitely ignore the data and len arguments

View File

@ -79,9 +79,9 @@ static bool ltm_template_next_placeholder(ltm_placeholder *ph, const char *s,
size_t ident_len = temp - ident; size_t ident_len = temp - ident;
if (strncmp("start", ident, ident_len) == 0) { if (strncmp("start", ident, ident_len) == 0) {
ph->type = ltm_placeholder_type_loop_start; ph->type = ltm_placeholder_type_nested_start;
} else if (strncmp("end", ident, ident_len) == 0) { } else if (strncmp("end", ident, ident_len) == 0) {
ph->type = ltm_placeholder_type_loop_end; ph->type = ltm_placeholder_type_nested_end;
} else { } else {
ph->type = ltm_placeholder_type_invalid; 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_RES(ltm_template_init(&template));
ltm_placeholder ph; ltm_placeholder ph;
bool in_loop = false; bool in_nested = false;
const char *loop_start = NULL, *loop_name = NULL; const char *nested_start = NULL, *nested_name = NULL;
size_t loop_depth = 0, loop_name_len = 0; size_t nested_depth = 0, nested_name_len = 0;
size_t cur_loop_depth = 0; size_t cur_nested_depth = 0;
while (ltm_template_next_placeholder(&ph, s, len)) { while (ltm_template_next_placeholder(&ph, s, len)) {
// Add part before placeholder as literal // Add part before placeholder as literal
if (!in_loop && (ph.start != s)) { if (!in_nested && (ph.start != s)) {
LTM_RES(ltm_template_block_append( LTM_RES(ltm_template_block_append(
template, ltm_template_block_type_literal, (void *)s, ph.start - s)); template, ltm_template_block_type_literal, (void *)s, ph.start - s));
} }
@ -113,44 +113,45 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
case ltm_placeholder_type_invalid: case ltm_placeholder_type_invalid:
return ltm_err_invalid_template; return ltm_err_invalid_template;
case ltm_placeholder_type_var: case ltm_placeholder_type_var:
if (!in_loop) { if (!in_nested) {
LTM_RES(ltm_template_block_append(template, ltm_template_block_type_var, LTM_RES(ltm_template_block_append(template, ltm_template_block_type_var,
NULL, 0)); NULL, 0));
LTM_RES(ltm_template_name_append(template, ph.name.s, ph.name.len, LTM_RES(ltm_template_name_append(template, ph.name.s, ph.name.len,
template->blocks.len - 1)); template->blocks.len - 1));
} }
break; break;
case ltm_placeholder_type_loop_start: case ltm_placeholder_type_nested_start:
if (!in_loop) { if (!in_nested) {
loop_start = ph.end + 1; nested_start = ph.end + 1;
loop_depth = cur_loop_depth; nested_depth = cur_nested_depth;
loop_name = ph.name.s; nested_name = ph.name.s;
loop_name_len = ph.name.len; nested_name_len = ph.name.len;
in_loop = true; in_nested = true;
} }
cur_loop_depth++; cur_nested_depth++;
break; break;
case ltm_placeholder_type_loop_end: case ltm_placeholder_type_nested_end:
// This means there's more loop end placeholders than loop starts // This means there's more nested end placeholders than nested starts
if (cur_loop_depth == 0) { if (cur_nested_depth == 0) {
return ltm_err_invalid_template; return ltm_err_invalid_template;
} }
cur_loop_depth--; cur_nested_depth--;
if (in_loop && (cur_loop_depth == loop_depth)) { if (in_nested && (cur_nested_depth == nested_depth)) {
size_t loop_len = ph.start - loop_start; size_t nested_len = ph.start - nested_start;
ltm_template *loop_template; ltm_template *nested_template;
LTM_RES(ltm_template_compile_n(&loop_template, loop_start, loop_len)); LTM_RES(
ltm_template_compile_n(&nested_template, nested_start, nested_len));
LTM_RES(ltm_template_block_append( LTM_RES(ltm_template_block_append(
template, ltm_template_block_type_loop, loop_template, 0)); template, ltm_template_block_type_nested, nested_template, 0));
LTM_RES(ltm_template_name_append(template, loop_name, loop_name_len, LTM_RES(ltm_template_name_append(template, nested_name, nested_name_len,
template->blocks.len - 1)); template->blocks.len - 1));
in_loop = false; in_nested = false;
} }
// We encountered a loop end without a start // We encountered a nested end without a start
else if (!in_loop) { else if (!in_nested) {
return ltm_err_invalid_template; return ltm_err_invalid_template;
} }
break; break;
@ -160,8 +161,8 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
s = ph.end + 1; s = ph.end + 1;
} }
// Unfinished loop // Unfinished nested
if (in_loop) { if (in_nested) {
return ltm_err_invalid_template; return ltm_err_invalid_template;
} }

View File

@ -50,7 +50,7 @@ void test_single_placeholder_trailing() {
TEST_CHECK(template->names.arr[0].index == 1); TEST_CHECK(template->names.arr[0].index == 1);
} }
void test_single_loop() { void test_single_nested() {
const char *s = "abc {{ l start }}some content {{ var }} {{ l end }}"; const char *s = "abc {{ l start }}some content {{ var }} {{ l end }}";
ltm_template *template; ltm_template *template;
@ -67,22 +67,22 @@ void test_single_loop() {
TEST_CHECK(template->blocks.arr[0].data.ptr == s); TEST_CHECK(template->blocks.arr[0].data.ptr == s);
TEST_CHECK(template->blocks.arr[0].data.len == 4); TEST_CHECK(template->blocks.arr[0].data.len == 4);
TEST_CHECK(template->blocks.arr[1].type == ltm_template_block_type_loop); TEST_CHECK(template->blocks.arr[1].type == ltm_template_block_type_nested);
ltm_template *loop_template = template->blocks.arr[1].data.ptr; ltm_template *nested_template = template->blocks.arr[1].data.ptr;
TEST_CHECK(loop_template->blocks.len == 3); TEST_CHECK(nested_template->blocks.len == 3);
TEST_CHECK(loop_template->blocks.arr[0].type == ltm_template_block_type_literal); TEST_CHECK(nested_template->blocks.arr[0].type == ltm_template_block_type_literal);
TEST_CHECK(loop_template->blocks.arr[0].data.ptr == s + 17); TEST_CHECK(nested_template->blocks.arr[0].data.ptr == s + 17);
TEST_CHECK(loop_template->blocks.arr[0].data.len == 13); TEST_CHECK(nested_template->blocks.arr[0].data.len == 13);
TEST_CHECK(loop_template->blocks.arr[1].type == ltm_template_block_type_var); TEST_CHECK(nested_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(nested_template->blocks.arr[2].type == ltm_template_block_type_literal);
TEST_CHECK(loop_template->blocks.arr[2].data.ptr == s + 39); TEST_CHECK(nested_template->blocks.arr[2].data.ptr == s + 39);
TEST_CHECK(loop_template->blocks.arr[2].data.len == 1); TEST_CHECK(nested_template->blocks.arr[2].data.len == 1);
TEST_CHECK(loop_template->names.len == 1); TEST_CHECK(nested_template->names.len == 1);
TEST_CHECK(loop_template->names.arr[0].name.s == s + 33); TEST_CHECK(nested_template->names.arr[0].name.s == s + 33);
TEST_CHECK(loop_template->names.arr[0].name.len == 3); TEST_CHECK(nested_template->names.arr[0].name.len == 3);
TEST_CHECK(loop_template->names.arr[0].index == 1); TEST_CHECK(nested_template->names.arr[0].index == 1);
} }
void test_unclosed_placeholder() { void test_unclosed_placeholder() {
@ -95,7 +95,7 @@ void test_unclosed_placeholder() {
TEST_CHECK(ltm_template_compile(&template, s) == ltm_err_invalid_template); TEST_CHECK(ltm_template_compile(&template, s) == ltm_err_invalid_template);
} }
void test_unclosed_loop() { void test_unclosed_nested() {
ltm_template *template; ltm_template *template;
const char *s = "abc {{ var start }} {{ hello }}"; const char *s = "abc {{ var start }} {{ hello }}";
@ -105,8 +105,8 @@ void test_unclosed_loop() {
TEST_LIST = { TEST_LIST = {
{ "template single placeholder", test_single_placeholder }, { "template single placeholder", test_single_placeholder },
{ "template single placeholder trailing", test_single_placeholder_trailing }, { "template single placeholder trailing", test_single_placeholder_trailing },
{ "template single loop", test_single_loop }, { "template single nested", test_single_nested },
{ "template unclosed placeholder", test_unclosed_placeholder }, { "template unclosed placeholder", test_unclosed_placeholder },
{ "template unclosed loop", test_unclosed_loop }, { "template unclosed nested", test_unclosed_nested },
{ NULL, NULL } { NULL, NULL }
}; };