feat(ltm): basic parsing of vars + some tests
parent
0bc8fc8273
commit
af5e519663
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 }
|
||||||
|
};
|
Loading…
Reference in New Issue