feat(ltm): basic parsing of vars + some tests
							parent
							
								
									0bc8fc8273
								
							
						
					
					
						commit
						af5e519663
					
				| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.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,
 | 
			
		||||
                                   size_t len) {
 | 
			
		||||
  if (len == 0) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ph->start = memchr(s, '{', len - 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;
 | 
			
		||||
  const char *loop_start = NULL;
 | 
			
		||||
  size_t 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)) {
 | 
			
		||||
    // 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) {
 | 
			
		||||
    case ltm_placeholder_type_invalid:
 | 
			
		||||
      return ltm_err_invalid_template;
 | 
			
		||||
    case ltm_placeholder_type_var:
 | 
			
		||||
      // TODO add var block
 | 
			
		||||
      break;
 | 
			
		||||
    case ltm_placeholder_type_var: {
 | 
			
		||||
      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_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:
 | 
			
		||||
      if (!in_loop) {
 | 
			
		||||
        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++;
 | 
			
		||||
      break;
 | 
			
		||||
    case ltm_placeholder_type_loop_end:
 | 
			
		||||
      if (cur_loop_depth == 0) {
 | 
			
		||||
        return ltm_err_invalid_template;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      cur_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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    len -= ph.end + 1 - ph.start;
 | 
			
		||||
    len -= ph.end + 1 - s;
 | 
			
		||||
    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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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