feat(ltm): basic parsing of vars + some tests

ltm
Jef Roosens 2023-12-15 10:59:52 +01:00 committed by Chewing_Bever
parent 0bc8fc8273
commit af5e519663
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
2 changed files with 133 additions and 8 deletions

View File

@ -1,4 +1,5 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include "ltm/common.h" #include "ltm/common.h"
@ -19,6 +20,10 @@ ltm_err ltm_template_init(ltm_template **out) {
bool ltm_template_next_placeholder(ltm_placeholder *ph, const char *s, bool ltm_template_next_placeholder(ltm_placeholder *ph, const char *s,
size_t len) { size_t len) {
if (len == 0) {
return false;
}
ph->start = memchr(s, '{', len - 1); ph->start = memchr(s, '{', len - 1);
if ((ph->start != NULL) && (ph->start[1] == '{')) { if ((ph->start != NULL) && (ph->start[1] == '{')) {
@ -97,19 +102,59 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
bool in_loop = false; bool in_loop = false;
const char *loop_start = NULL; const char *loop_start = NULL;
size_t loop_depth = 0; size_t loop_depth = 0;
size_t cur_loop_depth = 0; size_t cur_loop_depth = 0;
// TODO to ensure the loops are balanced, we should count how many loop starts
// we have seen and only match a loop end if the number matches; this way, we
// can allow arbitrarily nested loops
while (ltm_template_next_placeholder(&ph, s, len)) { while (ltm_template_next_placeholder(&ph, s, len)) {
// Add part before placeholder as literal
if (!in_loop && (ph.start != s)) {
ltm_template_block *blocks =
realloc(template->blocks.arr,
(template->blocks.len + 1) * sizeof(ltm_template_block));
if (blocks == NULL) {
return ltm_err_failed_alloc;
}
blocks[template->blocks.len].type = ltm_template_block_type_literal;
blocks[template->blocks.len].data.s = s;
blocks[template->blocks.len].len = ph.start - s;
template->blocks.arr = blocks;
template->blocks.len++;
}
switch (ph.type) { switch (ph.type) {
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: {
// TODO add var block ltm_template_block *blocks =
break; realloc(template->blocks.arr,
(template->blocks.len + 1) * sizeof(ltm_template_block));
if (blocks == NULL) {
return ltm_err_failed_alloc;
}
blocks[template->blocks.len].type = ltm_template_block_type_var;
template->blocks.arr = blocks;
ltm_template_block_name *names =
realloc(template->names.arr,
(template->names.len + 1) * sizeof(ltm_template_block_name));
if (names == NULL) {
return ltm_err_failed_alloc;
}
names[template->names.len].name.s = ph.name.s;
names[template->names.len].name.len = ph.name.len;
names[template->names.len].index = template->blocks.len;
template->names.arr = names;
template->blocks.len++;
template->names.len++;
} break;
case ltm_placeholder_type_loop_start: case ltm_placeholder_type_loop_start:
if (!in_loop) { if (!in_loop) {
loop_start = ph.end + 1; loop_start = ph.end + 1;
@ -119,6 +164,10 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
cur_loop_depth++; cur_loop_depth++;
break; break;
case ltm_placeholder_type_loop_end: case ltm_placeholder_type_loop_end:
if (cur_loop_depth == 0) {
return ltm_err_invalid_template;
}
cur_loop_depth--; cur_loop_depth--;
if (in_loop && (cur_loop_depth == loop_depth)) { if (in_loop && (cur_loop_depth == loop_depth)) {
@ -134,7 +183,7 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
break; break;
} }
len -= ph.end + 1 - ph.start; len -= ph.end + 1 - s;
s = ph.end + 1; s = ph.end + 1;
} }
@ -143,6 +192,26 @@ ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
return ltm_err_invalid_template; return ltm_err_invalid_template;
} }
// Add remaining trailing literal
if (len > 0) {
ltm_template_block *blocks =
realloc(template->blocks.arr,
(template->blocks.len + 1) * sizeof(ltm_template_block));
if (blocks == NULL) {
return ltm_err_failed_alloc;
}
blocks[template->blocks.len].type = ltm_template_block_type_literal;
blocks[template->blocks.len].data.s = s;
blocks[template->blocks.len].len = len;
template->blocks.arr = blocks;
template->blocks.len++;
}
*out = template;
return ltm_err_ok; return ltm_err_ok;
} }

56
ltm/test/compile.c 100644
View File

@ -0,0 +1,56 @@
#include "test.h"
#include "ltm/template.h"
#include "ltm/template_internal.h"
void test_single_placeholder() {
const char *s = "Hello, {{ world }}";
ltm_template *template;
TEST_CHECK(ltm_template_compile(&template, s) == ltm_err_ok);
TEST_CHECK(template->blocks.len == 2);
TEST_CHECK(template->blocks.arr[0].type == ltm_template_block_type_literal);
TEST_CHECK(template->blocks.arr[0].data.s == s);
TEST_CHECK(template->blocks.arr[0].len == 7);
TEST_CHECK(template->blocks.arr[1].type == ltm_template_block_type_var);
TEST_CHECK(template->names.len == 1);
TEST_CHECK(template->names.arr[0].name.s == s + 10);
TEST_CHECK(template->names.arr[0].name.len == 5);
TEST_CHECK(template->names.arr[0].index == 1);
}
void test_single_placeholder_trailing() {
const char *s = "Hello, {{ world }}!";
ltm_template *template;
TEST_CHECK(ltm_template_compile(&template, s) == ltm_err_ok);
TEST_CHECK(template->blocks.len == 3);
TEST_CHECK(template->blocks.arr[0].type == ltm_template_block_type_literal);
TEST_CHECK(template->blocks.arr[0].data.s == s);
TEST_CHECK(template->blocks.arr[0].len == 7);
TEST_CHECK(template->blocks.arr[1].type == ltm_template_block_type_var);
TEST_CHECK(template->blocks.arr[2].type == ltm_template_block_type_literal);
TEST_CHECK(template->blocks.arr[2].data.s == s + 18);
TEST_CHECK(template->blocks.arr[2].len == 1);
TEST_CHECK(template->names.len == 1);
TEST_CHECK(template->names.arr[0].name.s == s + 10);
TEST_CHECK(template->names.arr[0].name.len == 5);
TEST_CHECK(template->names.arr[0].index == 1);
}
TEST_LIST = {
{ "template single placeholder", test_single_placeholder },
{ "template single placeholder trailing", test_single_placeholder_trailing },
{ NULL, NULL }
};