fix: hectic C: template parse
This commit is contained in:
@@ -123,16 +123,16 @@ Includes content from other templates.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Function Tags
|
## Execution Tags
|
||||||
**Note:** Currently not included in C library; implemented as a wrapper on applicable platforms.
|
**Note:** Currently not included in C library; implemented as a wrapper on applicable platforms.
|
||||||
Enables calling functions with arguments.
|
Enables calling functions with arguments, or execute code. Have hardcoded context var - alows use template context
|
||||||
- **Prefix**
|
- **Prefix**
|
||||||
Denotes a function call.
|
Denotes a function call.
|
||||||
*Example:* `exec` | *(Empty)*
|
*Example:* `exec` | *(Empty)*
|
||||||
|
|
||||||
*Function Example:*
|
*Function Example:*
|
||||||
```tpl
|
```tpl
|
||||||
{% exec my_function(arg1, arg2, 'literal') %}
|
{% exec RETURN my_function(context->arg1, context->arg2, 'literal') %}
|
||||||
{% exec RETURN 'aaaaa' %}
|
{% exec RETURN 'aaaaa' %}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -143,3 +143,32 @@ Enables calling functions with arguments.
|
|||||||
- **Missing Fields/Functions/Templates:** Configurable to either return an error or warning.
|
- **Missing Fields/Functions/Templates:** Configurable to either return an error or warning.
|
||||||
- **Circular Includes:** Detect when possible.
|
- **Circular Includes:** Detect when possible.
|
||||||
- **No Shadowing:** Variables defined in section tags must not conflict with context variable names, otherwise, return an error.
|
- **No Shadowing:** Variables defined in section tags must not conflict with context variable names, otherwise, return an error.
|
||||||
|
|
||||||
|
|
||||||
|
## Shared example
|
||||||
|
```tpl
|
||||||
|
<div>text before<div>
|
||||||
|
|
||||||
|
{% include inner_template %}
|
||||||
|
|
||||||
|
{% name %}
|
||||||
|
|
||||||
|
{% for item in array do
|
||||||
|
some text: {% name2 %}
|
||||||
|
{% item.name %}
|
||||||
|
%}
|
||||||
|
|
||||||
|
<div>code insertion:</div>
|
||||||
|
{% execute
|
||||||
|
context + '{"name3": "zalupa"}';
|
||||||
|
|
||||||
|
IF context->condition THEN
|
||||||
|
RAISE INFO 'some log';
|
||||||
|
|
||||||
|
RETURN 'some text';
|
||||||
|
END
|
||||||
|
RETURN 'some other text';
|
||||||
|
%}
|
||||||
|
|
||||||
|
<div id="footer">...</div>
|
||||||
|
```
|
||||||
@@ -2241,36 +2241,70 @@ char *log_rules_to_debug_str__(CTX_DECLARATION, char *name, LogRule *self, PtrSe
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// -- View --
|
||||||
|
// ----------
|
||||||
|
|
||||||
|
View view_create(const void *data, size_t len, size_t isize) {
|
||||||
|
View view = { .data = data, .len = len, .isize = isize };
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
View string_to_view(const char *str) {
|
||||||
|
return view_create(str, strlen(str), sizeof(char));
|
||||||
|
}
|
||||||
|
|
||||||
|
View *string_to_view_ptr__(POSITION_INFO_DECLARATION, Arena *arena, const char *str) {
|
||||||
|
View *view = arena_alloc__(POSITION_INFO, arena, sizeof(View));
|
||||||
|
const View tmp = string_to_view(str);
|
||||||
|
*(void **)&view->data = (void *)tmp.data;
|
||||||
|
*(size_t *)&view->len = tmp.len;
|
||||||
|
*(size_t *)&view->isize = tmp.isize;
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------
|
// ---------------
|
||||||
// -- Templater --
|
// -- Templater --
|
||||||
// ---------------
|
// ---------------
|
||||||
|
|
||||||
// Look at package\c\hectic\docs\templater.md
|
// Look at package\c\hectic\docs\templater.md
|
||||||
|
|
||||||
TemplateConfig template_default_config__(POSITION_INFO_DECLARATION) {
|
TemplateConfig template_default_config__(POSITION_INFO_DECLARATION, Arena *arena) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "TEMPLATE: Default config");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "TEMPLATE: Default config");
|
||||||
TemplateConfig config;
|
TemplateConfig config = {
|
||||||
|
.Syntax = {
|
||||||
config.Syntax.Braces.open = "{%";
|
.Braces = {
|
||||||
config.Syntax.Braces.close = "%}";
|
.open = string_to_view_ptr__(POSITION_INFO, arena, "{%"),
|
||||||
config.Syntax.Section.control = "for ";
|
.close = string_to_view_ptr__(POSITION_INFO, arena, "%}")
|
||||||
config.Syntax.Section.source = " in ";
|
},
|
||||||
config.Syntax.Section.begin = " do ";
|
.Section = {
|
||||||
config.Syntax.Interpolate.invoke = "";
|
.control = string_to_view_ptr__(POSITION_INFO, arena, "for "),
|
||||||
config.Syntax.Include.invoke = "include ";
|
.source = string_to_view_ptr__(POSITION_INFO, arena, " in "),
|
||||||
config.Syntax.Execute.invoke = "exec ";
|
.begin = string_to_view_ptr__(POSITION_INFO, arena, " do ")
|
||||||
config.Syntax.nesting = "->";
|
},
|
||||||
|
.Interpolate = {
|
||||||
|
.invoke = string_to_view_ptr__(POSITION_INFO, arena, "")
|
||||||
|
},
|
||||||
|
.Include = {
|
||||||
|
.invoke = string_to_view_ptr__(POSITION_INFO, arena, "include ")
|
||||||
|
},
|
||||||
|
.Execute = {
|
||||||
|
.invoke = string_to_view_ptr__(POSITION_INFO, arena, "exec ")
|
||||||
|
},
|
||||||
|
.nesting = string_to_view_ptr__(POSITION_INFO, arena, "->")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_CONFIG_STR(field, name) \
|
#define CHECK_CONFIG_STR(field, name) \
|
||||||
do { \
|
do { \
|
||||||
if (config->Syntax.field == NULL) { \
|
if (config->Syntax.field->data == NULL) { \
|
||||||
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "VALIDATE: " name " is NULL"); \
|
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "VALIDATE: " name " is NULL"); \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
if (strlen(config->Syntax.field) > TEMPLATE_MAX_PREFIX_LEN) { \
|
if (config->Syntax.field->len > TEMPLATE_MAX_PREFIX_LEN) { \
|
||||||
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "VALIDATE: " name " is too long"); \
|
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "VALIDATE: " name " is too long"); \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
@@ -2299,42 +2333,95 @@ bool template_validate_config__(POSITION_INFO_DECLARATION, const TemplateConfig
|
|||||||
#undef CHECK_CONFIG_STR
|
#undef CHECK_CONFIG_STR
|
||||||
|
|
||||||
#define TEMPLATE_ASSERT_SYNTAX(pattern, message_arg, code_arg) \
|
#define TEMPLATE_ASSERT_SYNTAX(pattern, message_arg, code_arg) \
|
||||||
if (strncmp(*s, pattern, strlen(pattern))) { \
|
if (strncmp(*s, pattern, strlen(pattern)) == 0) { \
|
||||||
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "PARSE: " message_arg); \
|
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "PARSE: " message_arg); \
|
||||||
return RESULT_ERROR(TemplateResult, code_arg, message_arg); \
|
return RESULT_ERROR(TemplateResult, code_arg, message_arg); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TemplateValue init_template_value__(POSITION_INFO_DECLARATION, TemplateNodeType type) {
|
||||||
|
TemplateValue value;
|
||||||
|
switch (type) {
|
||||||
|
case TEMPLATE_NODE_TEXT:
|
||||||
|
value.text.content = NULL;
|
||||||
|
break;
|
||||||
|
case TEMPLATE_NODE_INTERPOLATE:
|
||||||
|
value.interpolate.key = NULL;
|
||||||
|
break;
|
||||||
|
case TEMPLATE_NODE_SECTION:
|
||||||
|
value.section.iterator = NULL;
|
||||||
|
value.section.collection = NULL;
|
||||||
|
value.section.body = NULL;
|
||||||
|
break;
|
||||||
|
case TEMPLATE_NODE_EXECUTE:
|
||||||
|
value.execute.code = NULL;
|
||||||
|
break;
|
||||||
|
case TEMPLATE_NODE_INCLUDE:
|
||||||
|
value.include.key = NULL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "INIT: Unknown node type");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateNode init_template_node__(POSITION_INFO_DECLARATION, Arena *arena, TemplateNodeType type) {
|
||||||
|
TemplateNode node;
|
||||||
|
node.next = NULL;
|
||||||
|
node.type = type;
|
||||||
|
node.value = arena_alloc__(POSITION_INFO, arena, sizeof(TemplateValue));
|
||||||
|
*node.value = init_template_value__(POSITION_INFO, type);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateResult init_template_result__(POSITION_INFO_DECLARATION, Arena *arena, TemplateNodeType type) {
|
||||||
|
TemplateResult result;
|
||||||
|
result.type = RESULT_SOME;
|
||||||
|
result.Result.some = arena_alloc__(POSITION_INFO, arena, sizeof(TemplateNode));
|
||||||
|
*result.Result.some = init_template_node__(POSITION_INFO, arena, type);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateNode new_template_node__(TemplateNodeType type, TemplateValue *value) {
|
||||||
|
TemplateNode node;
|
||||||
|
node.next = NULL;
|
||||||
|
node.type = type;
|
||||||
|
node.value = value;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
TemplateResult template_parse__(POSITION_INFO_DECLARATION, Arena *arena, const char **s, const TemplateConfig *config);
|
TemplateResult template_parse__(POSITION_INFO_DECLARATION, Arena *arena, const char **s, const TemplateConfig *config);
|
||||||
|
|
||||||
TemplateResult template_parse_interpolation__(POSITION_INFO_DECLARATION, Arena *arena, const char **s_ptr, const TemplateConfig *config) {
|
TemplateResult template_parse_interpolation__(POSITION_INFO_DECLARATION, Arena *arena, const char **s_ptr, const TemplateConfig *config) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Interpolation");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Interpolation");
|
||||||
|
|
||||||
TemplateResult result;
|
TemplateResult result = init_template_result__(POSITION_INFO, arena, TEMPLATE_NODE_INTERPOLATE);
|
||||||
|
|
||||||
const char **s = s_ptr;
|
const char **s = s_ptr;
|
||||||
|
|
||||||
// Skip to the content of the interpolation
|
// Skip to the content of the interpolation
|
||||||
*s += strlen(config->Syntax.Braces.open);
|
*s += config->Syntax.Braces.open->len;
|
||||||
*s = skip_whitespace(*s);
|
*s = skip_whitespace(*s);
|
||||||
*s += strlen(config->Syntax.Interpolate.invoke);
|
*s += config->Syntax.Interpolate.invoke->len;
|
||||||
|
|
||||||
*s = skip_whitespace(*s);
|
*s = skip_whitespace(*s);
|
||||||
const char *key_start = *s;
|
const char *key_start = *s;
|
||||||
|
|
||||||
while (isalnum(**s)) {
|
while (**s != '\0') {
|
||||||
if (**s == ' ' || strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close))) break;
|
if (isspace(**s) || strncmp(*s, config->Syntax.Braces.close->data, config->Syntax.Braces.close->len) == 0) break;
|
||||||
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open, "Nested tag in interpolation", TEMPLATE_ERROR_NESTED_INTERPOLATION);
|
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open->data, "Nested tag in interpolation", TEMPLATE_ERROR_NESTED_INTERPOLATION);
|
||||||
|
|
||||||
(*s)++;
|
(*s)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t key_len = *s - key_start;
|
size_t key_len = *s - key_start;
|
||||||
result.type = RESULT_SOME;
|
|
||||||
|
|
||||||
result.Result.some.value.interpolate.key = arena_strncpy__(POSITION_INFO, arena, key_start, key_len);
|
char *key = arena_strncpy__(POSITION_INFO, arena, key_start, key_len);
|
||||||
result.Result.some.type = TEMPLATE_NODE_INTERPOLATE;
|
|
||||||
|
|
||||||
*s_ptr = *s + strlen(config->Syntax.Braces.close);
|
result.Result.some->value->interpolate.key = key;
|
||||||
|
|
||||||
|
*s = skip_whitespace(*s);
|
||||||
|
*s_ptr = *s + config->Syntax.Braces.close->len;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -2342,46 +2429,44 @@ TemplateResult template_parse_interpolation__(POSITION_INFO_DECLARATION, Arena *
|
|||||||
TemplateResult template_parse_section__(POSITION_INFO_DECLARATION, Arena *arena, const char **s_ptr, const TemplateConfig *config) {
|
TemplateResult template_parse_section__(POSITION_INFO_DECLARATION, Arena *arena, const char **s_ptr, const TemplateConfig *config) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Section");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Section");
|
||||||
|
|
||||||
TemplateResult result;
|
TemplateResult result = init_template_result__(POSITION_INFO, arena, TEMPLATE_NODE_SECTION);
|
||||||
result.type = RESULT_SOME;
|
|
||||||
result.Result.some.type = TEMPLATE_NODE_SECTION;
|
|
||||||
|
|
||||||
const char **s = s_ptr;
|
const char **s = s_ptr;
|
||||||
|
|
||||||
// Skip to the content of the section
|
// Skip to the content of the section
|
||||||
*s += strlen(config->Syntax.Braces.open);
|
*s += config->Syntax.Braces.open->len;
|
||||||
*s = skip_whitespace(*s);
|
*s = skip_whitespace(*s);
|
||||||
*s += strlen(config->Syntax.Section.control);
|
*s += config->Syntax.Section.control->len;
|
||||||
|
|
||||||
// Find the iterator name
|
// Find the iterator name
|
||||||
*s = skip_whitespace(*s);
|
*s = skip_whitespace(*s);
|
||||||
const char *iterator_start = *s;
|
const char *iterator_start = *s;
|
||||||
|
|
||||||
while (isalnum(**s)) {
|
while (**s != '\0') {
|
||||||
if (**s == ' ' || **s == '\n' || **s == '\t' || strncmp(*s, config->Syntax.Section.source, strlen(config->Syntax.Section.source))) break;
|
if (**s == ' ' || **s == '\n' || **s == '\t' || strncmp(*s, config->Syntax.Section.source->data, config->Syntax.Section.source->len) == 0) break;
|
||||||
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.close, "Unexpected section end", TEMPLATE_ERROR_UNEXPECTED_SECTION_END);
|
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.close->data, "Unexpected section end", TEMPLATE_ERROR_UNEXPECTED_SECTION_END);
|
||||||
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open, "Nested tag in section element name", TEMPLATE_ERROR_NESTED_SECTION_ITERATOR);
|
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open->data, "Nested tag in section element name", TEMPLATE_ERROR_NESTED_SECTION_ITERATOR);
|
||||||
|
|
||||||
(*s)++;
|
(*s)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t iterator_len = *s - iterator_start;
|
size_t iterator_len = *s - iterator_start;
|
||||||
result.Result.some.value.section.iterator = arena_strncpy__(POSITION_INFO, arena, iterator_start, iterator_len);
|
result.Result.some->value->section.iterator = arena_strncpy__(POSITION_INFO, arena, iterator_start, iterator_len);
|
||||||
|
|
||||||
// Find the collection name
|
// Find the collection name
|
||||||
*s = skip_whitespace(*s);
|
*s = skip_whitespace(*s);
|
||||||
const char *collection_start = *s;
|
const char *collection_start = *s;
|
||||||
|
|
||||||
while (isalnum(**s)) {
|
while (**s != '\0') {
|
||||||
if (**s == ' ' || **s == '\n' || **s == '\t' || strncmp(*s, config->Syntax.Section.begin, strlen(config->Syntax.Section.begin))) break;
|
if (**s == ' ' || **s == '\n' || **s == '\t' || strncmp(*s, config->Syntax.Section.begin->data, config->Syntax.Section.begin->len) == 0) break;
|
||||||
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.close, "Unexpected section end", TEMPLATE_ERROR_UNEXPECTED_SECTION_END);
|
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.close->data, "Unexpected section end", TEMPLATE_ERROR_UNEXPECTED_SECTION_END);
|
||||||
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open, "Nested tag in section iterator", TEMPLATE_ERROR_NESTED_SECTION_ITERATOR);
|
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open->data, "Nested tag in section iterator", TEMPLATE_ERROR_NESTED_SECTION_ITERATOR);
|
||||||
|
|
||||||
(*s)++;
|
(*s)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t collection_len = *s - collection_start;
|
size_t collection_len = *s - collection_start;
|
||||||
result.Result.some.value.section.collection = arena_strncpy__(POSITION_INFO, arena, collection_start, collection_len);
|
result.Result.some->value->section.collection = arena_strncpy__(POSITION_INFO, arena, collection_start, collection_len);
|
||||||
|
|
||||||
// Parse the body
|
// Parse the body
|
||||||
TemplateResult body_result = template_parse__(POSITION_INFO, arena, s, config);
|
TemplateResult body_result = template_parse__(POSITION_INFO, arena, s, config);
|
||||||
@@ -2389,40 +2474,39 @@ TemplateResult template_parse_section__(POSITION_INFO_DECLARATION, Arena *arena,
|
|||||||
return body_result;
|
return body_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Result.some.value.section.body = &body_result.Result.some;
|
result.Result.some->value->section.body = body_result.Result.some;
|
||||||
|
|
||||||
*s_ptr = *s + strlen(config->Syntax.Braces.close);
|
*s_ptr = *s + config->Syntax.Braces.close->len;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TemplateResult template_parse_include__(POSITION_INFO_DECLARATION, Arena *arena, const char **s_ptr, const TemplateConfig *config) {
|
TemplateResult template_parse_include__(POSITION_INFO_DECLARATION, Arena *arena, const char **s_ptr, const TemplateConfig *config) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Include");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Include");
|
||||||
TemplateResult result;
|
|
||||||
result.type = RESULT_SOME;
|
TemplateResult result = init_template_result__(POSITION_INFO, arena, TEMPLATE_NODE_INCLUDE);
|
||||||
result.Result.some.type = TEMPLATE_NODE_INCLUDE;
|
|
||||||
|
|
||||||
const char **s = s_ptr;
|
const char **s = s_ptr;
|
||||||
|
|
||||||
// Skip to the content of the include
|
// Skip to the content of the include
|
||||||
*s += strlen(config->Syntax.Braces.open);
|
*s += config->Syntax.Braces.open->len;
|
||||||
*s = skip_whitespace(*s);
|
*s = skip_whitespace(*s);
|
||||||
*s += strlen(config->Syntax.Include.invoke);
|
*s += config->Syntax.Include.invoke->len;
|
||||||
|
|
||||||
*s = skip_whitespace(*s);
|
*s = skip_whitespace(*s);
|
||||||
const char *include_start = *s;
|
const char *include_start = *s;
|
||||||
|
|
||||||
while (isalnum(**s)) {
|
while (**s != '\0') {
|
||||||
if (**s == ' ' || **s == '\n' || **s == '\t' || strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close))) break;
|
if (**s == ' ' || **s == '\n' || **s == '\t' || strncmp(*s, config->Syntax.Braces.close->data, config->Syntax.Braces.close->len) == 0) break;
|
||||||
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open, "Nested tag in include", TEMPLATE_ERROR_NESTED_INCLUDE);
|
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open->data, "Nested tag in include", TEMPLATE_ERROR_NESTED_INCLUDE);
|
||||||
|
|
||||||
(*s)++;
|
(*s)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t include_len = *s - include_start;
|
size_t include_len = *s - include_start;
|
||||||
result.Result.some.value.include.key = arena_strncpy__(POSITION_INFO, arena, include_start, include_len);
|
result.Result.some->value->include.key = arena_strncpy__(POSITION_INFO, arena, include_start, include_len);
|
||||||
|
|
||||||
*s_ptr = *s + strlen(config->Syntax.Braces.close);
|
*s_ptr = *s + config->Syntax.Braces.close->len;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -2430,28 +2514,26 @@ TemplateResult template_parse_include__(POSITION_INFO_DECLARATION, Arena *arena,
|
|||||||
TemplateResult template_parse_execute__(POSITION_INFO_DECLARATION, Arena *arena, const char **s_ptr, const TemplateConfig *config) {
|
TemplateResult template_parse_execute__(POSITION_INFO_DECLARATION, Arena *arena, const char **s_ptr, const TemplateConfig *config) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Execute");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Execute");
|
||||||
|
|
||||||
TemplateResult result;
|
TemplateResult result = init_template_result__(POSITION_INFO, arena, TEMPLATE_NODE_EXECUTE);
|
||||||
result.type = RESULT_SOME;
|
|
||||||
result.Result.some.type = TEMPLATE_NODE_EXECUTE;
|
|
||||||
|
|
||||||
const char **s = s_ptr;
|
const char **s = s_ptr;
|
||||||
|
|
||||||
*s += strlen(config->Syntax.Braces.open);
|
*s += config->Syntax.Braces.open->len;
|
||||||
*s = skip_whitespace(*s);
|
*s = skip_whitespace(*s);
|
||||||
*s += strlen(config->Syntax.Execute.invoke);
|
*s += config->Syntax.Execute.invoke->len;
|
||||||
|
|
||||||
*s = skip_whitespace(*s);
|
*s = skip_whitespace(*s);
|
||||||
const char *code_start = *s;
|
const char *code_start = *s;
|
||||||
|
|
||||||
while (strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close))) {
|
while (strncmp(*s, config->Syntax.Braces.close->data, config->Syntax.Braces.close->len) == 0) {
|
||||||
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open, "Nested tag in execute", TEMPLATE_ERROR_NESTED_EXECUTE);
|
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open->data, "Nested tag in execute", TEMPLATE_ERROR_NESTED_EXECUTE);
|
||||||
(*s)++;
|
(*s)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t code_len = *s - code_start;
|
size_t code_len = *s - code_start;
|
||||||
result.Result.some.value.execute.code = arena_strncpy__(POSITION_INFO, arena, code_start, code_len);
|
result.Result.some->value->execute.code = arena_strncpy__(POSITION_INFO, arena, code_start, code_len);
|
||||||
|
|
||||||
*s_ptr = *s + strlen(config->Syntax.Braces.close);
|
*s_ptr = *s + config->Syntax.Braces.close->len;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -2472,62 +2554,129 @@ TemplateResult template_parse__(POSITION_INFO_DECLARATION, Arena *arena, const c
|
|||||||
const char *start = *s;
|
const char *start = *s;
|
||||||
|
|
||||||
TemplateNode *root = arena_alloc__(POSITION_INFO, arena, sizeof(TemplateNode));
|
TemplateNode *root = arena_alloc__(POSITION_INFO, arena, sizeof(TemplateNode));
|
||||||
|
*root = init_template_node__(POSITION_INFO, arena, TEMPLATE_NODE_TEXT);
|
||||||
|
|
||||||
TemplateNode *current = root;
|
TemplateNode *current = root;
|
||||||
|
bool current_node_filled = false;
|
||||||
|
|
||||||
int open_brace_len = strlen(config->Syntax.Braces.open);
|
int open_brace_len = config->Syntax.Braces.open->len;
|
||||||
|
|
||||||
while (*s) {
|
while (*s && **s != '\0') {
|
||||||
// Find the first open brace
|
if (strncmp(*s, config->Syntax.Braces.open->data, open_brace_len) == 0) {
|
||||||
if (strncmp(*s, config->Syntax.Braces.open, open_brace_len) == 0) {
|
|
||||||
// Add text node if there is any text before the tag
|
|
||||||
if (start != *s) {
|
if (start != *s) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Text node: %s", arena_strncpy__(POSITION_INFO, DISPOSABLE_ARENA, start, *s - start));
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Text node: %s", arena_strncpy__(POSITION_INFO, DISPOSABLE_ARENA, start, *s - start));
|
||||||
current->type = TEMPLATE_NODE_TEXT;
|
|
||||||
current->value.text.content = arena_strncpy__(POSITION_INFO, arena, start, *s - start);
|
if (current_node_filled) {
|
||||||
|
TemplateNode *new_node = arena_alloc__(POSITION_INFO, arena, sizeof(TemplateNode));
|
||||||
|
*new_node = init_template_node__(POSITION_INFO, arena, TEMPLATE_NODE_TEXT);
|
||||||
|
current->next = new_node;
|
||||||
|
current = new_node;
|
||||||
|
} else {
|
||||||
|
current->type = TEMPLATE_NODE_TEXT;
|
||||||
|
*current->value = init_template_value__(POSITION_INFO, TEMPLATE_NODE_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
current->value->text.content = arena_strncpy__(POSITION_INFO, arena, start, *s - start);
|
||||||
|
current_node_filled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deside tag type by prefix
|
// Determine tag type by prefix
|
||||||
TemplateResult current_result;
|
TemplateResult current_result;
|
||||||
{
|
{
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Found tag");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Found tag");
|
||||||
|
|
||||||
const char *tag_prefix = *s + open_brace_len;
|
const char *tag_prefix = *s + open_brace_len;
|
||||||
tag_prefix = skip_whitespace(tag_prefix);
|
tag_prefix = skip_whitespace(tag_prefix);
|
||||||
raise_trace("tag_prefix: %p", tag_prefix);
|
raise_trace("tag_prefix: %p", tag_prefix);
|
||||||
|
|
||||||
if (strncmp(tag_prefix, config->Syntax.Section.control, strlen(config->Syntax.Section.control)) == 0) {
|
typedef struct {
|
||||||
|
const View * const prefix;
|
||||||
|
int tag_type;
|
||||||
|
} PrefixMatch;
|
||||||
|
|
||||||
|
PrefixMatch matches[] = {
|
||||||
|
{config->Syntax.Section.control, 1},
|
||||||
|
{config->Syntax.Interpolate.invoke, 2},
|
||||||
|
{config->Syntax.Include.invoke, 3},
|
||||||
|
{config->Syntax.Execute.invoke, 4}
|
||||||
|
};
|
||||||
|
|
||||||
|
int matched_type = 0;
|
||||||
|
size_t max_length = 0;
|
||||||
|
|
||||||
|
// Find longest match (in case when one name of tage is part of another)
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (strncmp(tag_prefix, matches[i].prefix->data, matches[i].prefix->len) == 0) {
|
||||||
|
// NOTE(yukkop): >= becouse one of the strings may be ""
|
||||||
|
if (matches[i].prefix->len >= max_length) {
|
||||||
|
max_length = matches[i].prefix->len;
|
||||||
|
matched_type = matches[i].tag_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched_type == 1) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Section tag");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Section tag");
|
||||||
current_result = template_parse_section__(POSITION_INFO, arena, s, config);
|
current_result = template_parse_section__(POSITION_INFO, arena, s, config);
|
||||||
} else if (strncmp(tag_prefix, config->Syntax.Interpolate.invoke, strlen(config->Syntax.Interpolate.invoke)) == 0) {
|
start = *s;
|
||||||
|
} else if (matched_type == 2) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Interpolation tag");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Interpolation tag");
|
||||||
current_result = template_parse_interpolation__(POSITION_INFO, arena, s, config);
|
current_result = template_parse_interpolation__(POSITION_INFO, arena, s, config);
|
||||||
} else if (strncmp(tag_prefix, config->Syntax.Include.invoke, strlen(config->Syntax.Include.invoke)) == 0) {
|
start = *s;
|
||||||
|
} else if (matched_type == 3) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Include tag");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Include tag");
|
||||||
current_result = template_parse_include__(POSITION_INFO, arena, s, config);
|
current_result = template_parse_include__(POSITION_INFO, arena, s, config);
|
||||||
} else if (strncmp(tag_prefix, config->Syntax.Execute.invoke, strlen(config->Syntax.Execute.invoke)) == 0) {
|
start = *s;
|
||||||
|
} else if (matched_type == 4) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Execute tag");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Execute tag");
|
||||||
current_result = template_parse_execute__(POSITION_INFO, arena, s, config);
|
current_result = template_parse_execute__(POSITION_INFO, arena, s, config);
|
||||||
|
start = *s;
|
||||||
} else {
|
} else {
|
||||||
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "PARSE: Unknown tag prefix: %s", slice_create__(POSITION_INFO, 1, (char *)tag_prefix, strlen(tag_prefix), 0, TEMPLATE_MAX_PREFIX_LEN));
|
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "PARSE: Unknown tag prefix: %s", slice_create__(POSITION_INFO, 1, (char *)tag_prefix, strlen(tag_prefix), 0, TEMPLATE_MAX_PREFIX_LEN));
|
||||||
|
|
||||||
return RESULT_ERROR(TemplateResult, TEMPLATE_ERROR_UNKNOWN_TAG, "Unknown tag prefix");
|
return RESULT_ERROR(TemplateResult, TEMPLATE_ERROR_UNKNOWN_TAG, "Unknown tag prefix");
|
||||||
}
|
}
|
||||||
|
|
||||||
TRY(current_result);
|
TRY(current_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
*current = current_result.Result.some;
|
if (current_node_filled) {
|
||||||
current->next = arena_alloc__(POSITION_INFO, arena, sizeof(TemplateNode));
|
// SAFETY(yukkop): NO init necessary here
|
||||||
current = current->next;
|
TemplateNode *new_node = arena_alloc__(POSITION_INFO, arena, sizeof(TemplateNode));
|
||||||
|
*new_node = *current_result.Result.some;
|
||||||
|
current->next = new_node;
|
||||||
|
current = new_node;
|
||||||
|
} else {
|
||||||
|
*current = *current_result.Result.some;
|
||||||
|
}
|
||||||
|
current_node_filled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*s)++;
|
if (**s != '\0') {
|
||||||
|
(*s)++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add text node if there is any text after the last tag
|
// Add text node if there is any text after the last tag
|
||||||
if (start != *s) {
|
if (start != *s) {
|
||||||
current->type = TEMPLATE_NODE_TEXT;
|
if (current_node_filled) {
|
||||||
current->value.text.content = arena_strncpy__(POSITION_INFO, arena, start, *s - start);
|
TemplateNode *new_node = arena_alloc__(POSITION_INFO, arena, sizeof(TemplateNode));
|
||||||
|
*new_node = init_template_node__(POSITION_INFO, arena, TEMPLATE_NODE_TEXT);
|
||||||
|
current->next = new_node;
|
||||||
|
current = new_node;
|
||||||
|
} else {
|
||||||
|
current->type = TEMPLATE_NODE_TEXT;
|
||||||
|
*current->value = init_template_value__(POSITION_INFO, TEMPLATE_NODE_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
current->value->text.content = arena_strncpy__(POSITION_INFO, arena, start, *s - start);
|
||||||
|
current_node_filled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set null when node is not filled
|
||||||
|
if (!current_node_filled && current == root) {
|
||||||
|
root->type = TEMPLATE_NODE_TEXT;
|
||||||
|
*root->value = init_template_value__(POSITION_INFO, TEMPLATE_NODE_TEXT);
|
||||||
|
root->value->text.content = arena_strncpy__(POSITION_INFO, arena, "", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RESULT_SOME(TemplateResult, *root);
|
return RESULT_SOME(TemplateResult, *root);
|
||||||
@@ -2608,10 +2757,9 @@ char *template_value_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, con
|
|||||||
|
|
||||||
char *template_node_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, const char *name, const TemplateNode *self, PtrSet *visited) {
|
char *template_node_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, const char *name, const TemplateNode *self, PtrSet *visited) {
|
||||||
char *result = arena_alloc(arena, MEM_KiB);
|
char *result = arena_alloc(arena, MEM_KiB);
|
||||||
STRUCT_TO_DEBUG_STR(arena, result, TemplateNode, name, self, visited, 4,
|
STRUCT_TO_DEBUG_STR(arena, result, TemplateNode, name, self, visited, 3,
|
||||||
enum_to_debug_str__(POSITION_INFO, arena, "type", self->type, template_node_type_to_string(self->type)),
|
enum_to_debug_str__(POSITION_INFO, arena, "type", self->type, template_node_type_to_string(self->type)),
|
||||||
template_value_to_debug_str__(POSITION_INFO, arena, "value", &self->value, self->type, visited),
|
template_value_to_debug_str__(POSITION_INFO, arena, "value", self->value, self->type, visited),
|
||||||
template_node_to_debug_str__(POSITION_INFO, arena, "children", self->children, visited),
|
|
||||||
template_node_to_debug_str__(POSITION_INFO, arena, "next", self->next, visited)
|
template_node_to_debug_str__(POSITION_INFO, arena, "next", self->next, visited)
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
@@ -2644,38 +2792,29 @@ char *template_node_to_json_str__(POSITION_INFO_DECLARATION, Arena *arena, const
|
|||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case TEMPLATE_NODE_SECTION:
|
case TEMPLATE_NODE_SECTION:
|
||||||
APPEND("\"content\":{\"iterator\":\"%s\",\"collection\":\"%s\"}",
|
APPEND("\"content\":{\"iterator\":\"%s\",\"collection\":\"%s\"}",
|
||||||
node->value.section.iterator,
|
node->value->section.iterator,
|
||||||
node->value.section.collection);
|
node->value->section.collection);
|
||||||
char *body_str = template_node_to_json_str__(POSITION_INFO, arena, node->value.section.body, depth + 1);
|
char *body_str = template_node_to_json_str__(POSITION_INFO, arena, node->value->section.body, depth + 1);
|
||||||
if (body_str) {
|
if (body_str) {
|
||||||
APPEND(",\"body\":%s", body_str);
|
APPEND(",\"body\":%s", body_str);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TEMPLATE_NODE_INTERPOLATE:
|
case TEMPLATE_NODE_INTERPOLATE:
|
||||||
APPEND("\"content\":{\"key\":\"%s\"}", node->value.interpolate.key);
|
APPEND("\"content\":{\"key\":\"%s\"}", node->value->interpolate.key);
|
||||||
break;
|
break;
|
||||||
case TEMPLATE_NODE_EXECUTE:
|
case TEMPLATE_NODE_EXECUTE:
|
||||||
APPEND("\"content\":{\"code\":\"%s\"}", node->value.execute.code);
|
APPEND("\"content\":{\"code\":\"%s\"}", node->value->execute.code);
|
||||||
break;
|
break;
|
||||||
case TEMPLATE_NODE_INCLUDE:
|
case TEMPLATE_NODE_INCLUDE:
|
||||||
APPEND("\"content\":{\"key\":\"%s\"}", node->value.include.key);
|
APPEND("\"content\":{\"key\":\"%s\"}", node->value->include.key);
|
||||||
break;
|
break;
|
||||||
case TEMPLATE_NODE_TEXT:
|
case TEMPLATE_NODE_TEXT:
|
||||||
APPEND("\"content\":{\"content\":\"%s\"}", node->value.text.content);
|
APPEND("\"content\":{\"content\":\"%s\"}", node->value->text.content);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->children) {
|
|
||||||
APPEND(",\"children\":[");
|
|
||||||
char *child_str = template_node_to_json_str__(POSITION_INFO, arena, node->children, depth + 1);
|
|
||||||
if (child_str) {
|
|
||||||
APPEND(",%s", child_str);
|
|
||||||
}
|
|
||||||
APPEND("]");
|
|
||||||
}
|
|
||||||
|
|
||||||
APPEND("}");
|
APPEND("}");
|
||||||
|
|
||||||
if (node->next) {
|
if (node->next) {
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ typedef struct {
|
|||||||
ResultType type; \
|
ResultType type; \
|
||||||
union { \
|
union { \
|
||||||
HecticError error; \
|
HecticError error; \
|
||||||
some_type some; \
|
some_type *some; \
|
||||||
} Result; \
|
} Result; \
|
||||||
} name##Result
|
} name##Result
|
||||||
|
|
||||||
@@ -166,11 +166,11 @@ typedef struct {
|
|||||||
#define RESULT_ERROR_CODE(result) (result.Result.error.code)
|
#define RESULT_ERROR_CODE(result) (result.Result.error.code)
|
||||||
#define RESULT_ERROR_MESSAGE(result) (result.Result.error.message)
|
#define RESULT_ERROR_MESSAGE(result) (result.Result.error.message)
|
||||||
|
|
||||||
#define RESULT_SOME_VALUE(result) (result.Result.some)
|
#define RESULT_SOME_VALUE(result) (*result.Result.some)
|
||||||
#define RESULT_ERROR_VALUE(result) (result.Result.error)
|
#define RESULT_ERROR_VALUE(result) (result.Result.error)
|
||||||
|
|
||||||
#define RESULT_SOME(result_type, value) \
|
#define RESULT_SOME(result_type, value) \
|
||||||
(result_type) { .type = RESULT_SOME, .Result.some = value }
|
(result_type) { .type = RESULT_SOME, .Result.some = &value }
|
||||||
|
|
||||||
#define RESULT_ERROR(result_type, error_code, error_message) \
|
#define RESULT_ERROR(result_type, error_code, error_message) \
|
||||||
(result_type) { .type = RESULT_ERROR, .Result.error = { .code = error_code, .message = error_message } }
|
(result_type) { .type = RESULT_ERROR, .Result.error = { .code = error_code, .message = error_message } }
|
||||||
@@ -427,7 +427,7 @@ static Arena disposable_arena __attribute__((unused)) = {0};
|
|||||||
|
|
||||||
#define DISPOSABLE_ARENA __extension__ ({ \
|
#define DISPOSABLE_ARENA __extension__ ({ \
|
||||||
if (disposable_arena.begin == NULL) { \
|
if (disposable_arena.begin == NULL) { \
|
||||||
disposable_arena = arena_init__(__FILE__, __func__, __LINE__, MEM_MiB); \
|
disposable_arena = arena_init__(__FILE__, __func__, __LINE__, MEM_MiB * 8); \
|
||||||
} else { \
|
} else { \
|
||||||
arena_reset(&disposable_arena); \
|
arena_reset(&disposable_arena); \
|
||||||
} \
|
} \
|
||||||
@@ -726,6 +726,16 @@ char* slice_to_debug_str__(const char* file, const char* func, int line, Arena *
|
|||||||
|
|
||||||
#define slice_to_debug_str(arena, slice) slice_to_debug_str__(__FILE__, __func__, __LINE__, arena, slice)
|
#define slice_to_debug_str(arena, slice) slice_to_debug_str__(__FILE__, __func__, __LINE__, arena, slice)
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// -- View --
|
||||||
|
// ----------
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const void * const data;
|
||||||
|
const size_t len;
|
||||||
|
const size_t isize;
|
||||||
|
} View;
|
||||||
|
|
||||||
// ---------------
|
// ---------------
|
||||||
// -- Templater --
|
// -- Templater --
|
||||||
// ---------------
|
// ---------------
|
||||||
@@ -743,24 +753,24 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
struct {
|
struct {
|
||||||
struct {
|
struct {
|
||||||
const char *open; // Default: "{%"
|
const View * const open; // Default: "{%"
|
||||||
const char *close; // Default: "%}"
|
const View * const close; // Default: "%}"
|
||||||
} Braces;
|
} Braces;
|
||||||
struct {
|
struct {
|
||||||
const char *control; // default: "for "
|
const View * const control; // default: "for "
|
||||||
const char *source; // default: " in "
|
const View * const source; // default: " in "
|
||||||
const char *begin; // default: " do "
|
const View * const begin; // default: " do "
|
||||||
} Section;
|
} Section;
|
||||||
struct {
|
struct {
|
||||||
const char *invoke; // default: ""
|
const View * const invoke; // default: ""
|
||||||
} Interpolate;
|
} Interpolate;
|
||||||
struct {
|
struct {
|
||||||
const char *invoke; // default: "include "
|
const View * const invoke; // default: "include "
|
||||||
} Include;
|
} Include;
|
||||||
struct {
|
struct {
|
||||||
const char *invoke; // default: "exec "
|
const View * const invoke; // default: "exec "
|
||||||
} Execute;
|
} Execute;
|
||||||
const char *nesting; // default: "->"
|
const View * const nesting; // default: "->"
|
||||||
} Syntax;
|
} Syntax;
|
||||||
} TemplateConfig;
|
} TemplateConfig;
|
||||||
|
|
||||||
@@ -798,16 +808,15 @@ typedef union {
|
|||||||
|
|
||||||
struct TemplateNode {
|
struct TemplateNode {
|
||||||
TemplateNodeType type;
|
TemplateNodeType type;
|
||||||
TemplateValue value;
|
TemplateValue *value;
|
||||||
TemplateNode *children; // child nodes
|
TemplateNode *next;
|
||||||
TemplateNode *next; // sibling nodes
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RESULT(Template, TemplateNode);
|
RESULT(Template, TemplateNode);
|
||||||
|
|
||||||
TemplateResult template_parse__(const char *file, const char *func, int line, Arena *arena, const char **s, const TemplateConfig *config);
|
TemplateResult template_parse__(const char *file, const char *func, int line, Arena *arena, const char **s, const TemplateConfig *config);
|
||||||
|
|
||||||
TemplateConfig template_default_config__(const char *file, const char *func, int line);
|
TemplateConfig template_default_config__(const char *file, const char *func, int line, Arena *arena);
|
||||||
|
|
||||||
char *template_node_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *name, const TemplateNode *self, PtrSet *visited);
|
char *template_node_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *name, const TemplateNode *self, PtrSet *visited);
|
||||||
|
|
||||||
@@ -815,7 +824,7 @@ char *template_node_to_json_str__(const char *file, const char *func, int line,
|
|||||||
|
|
||||||
#define template_parse(arena, s, config) template_parse__(__FILE__, __func__, __LINE__, arena, s, config)
|
#define template_parse(arena, s, config) template_parse__(__FILE__, __func__, __LINE__, arena, s, config)
|
||||||
|
|
||||||
#define template_default_config() template_default_config__(__FILE__, __func__, __LINE__)
|
#define template_default_config(arena) template_default_config__(__FILE__, __func__, __LINE__, arena)
|
||||||
|
|
||||||
#define TEMPLATE_NODE_TO_DEBUG_STR(arena, name, node) \
|
#define TEMPLATE_NODE_TO_DEBUG_STR(arena, name, node) \
|
||||||
template_node_to_debug_str__(__FILE__, __func__, __LINE__, arena, name, node, ptrset_init(arena))
|
template_node_to_debug_str__(__FILE__, __func__, __LINE__, arena, name, node, ptrset_init(arena))
|
||||||
@@ -823,4 +832,9 @@ char *template_node_to_json_str__(const char *file, const char *func, int line,
|
|||||||
#define TEMPLATE_NODE_TO_JSON_STR(arena, node) \
|
#define TEMPLATE_NODE_TO_JSON_STR(arena, node) \
|
||||||
template_node_to_json_str__(__FILE__, __func__, __LINE__, arena, node, 0)
|
template_node_to_json_str__(__FILE__, __func__, __LINE__, arena, node, 0)
|
||||||
|
|
||||||
|
TemplateNode init_template_node__(const char *file, const char *func, int line, Arena *arena, TemplateNodeType type);
|
||||||
|
|
||||||
|
#define init_template_node(arena, type) \
|
||||||
|
init_template_node__(__FILE__, __func__, __LINE__, arena, type)
|
||||||
|
|
||||||
#endif // EPRINTF_H
|
#endif // EPRINTF_H
|
||||||
@@ -14,7 +14,6 @@
|
|||||||
" content = %p \"Hello\"\n" \
|
" content = %p \"Hello\"\n" \
|
||||||
" } %p\n" \
|
" } %p\n" \
|
||||||
" } %p,\n" \
|
" } %p,\n" \
|
||||||
" struct TemplateNode children = NULL,\n" \
|
|
||||||
" struct TemplateNode next = {\n" \
|
" struct TemplateNode next = {\n" \
|
||||||
" enum type = INTERPOLATE 1 ,\n" \
|
" enum type = INTERPOLATE 1 ,\n" \
|
||||||
" union TemplateValue value = {\n" \
|
" union TemplateValue value = {\n" \
|
||||||
@@ -22,7 +21,6 @@
|
|||||||
" key = %p \"name\"\n" \
|
" key = %p \"name\"\n" \
|
||||||
" } %p\n" \
|
" } %p\n" \
|
||||||
" } %p,\n" \
|
" } %p,\n" \
|
||||||
" struct TemplateNode children = NULL,\n" \
|
|
||||||
" struct TemplateNode next = {\n" \
|
" struct TemplateNode next = {\n" \
|
||||||
" enum type = TEXT 0 ,\n" \
|
" enum type = TEXT 0 ,\n" \
|
||||||
" union TemplateValue value = {\n" \
|
" union TemplateValue value = {\n" \
|
||||||
@@ -30,7 +28,6 @@
|
|||||||
" content = %p \"!\"\n" \
|
" content = %p \"!\"\n" \
|
||||||
" } %p\n" \
|
" } %p\n" \
|
||||||
" } %p,\n" \
|
" } %p,\n" \
|
||||||
" struct TemplateNode children = NULL,\n" \
|
|
||||||
" struct TemplateNode next = NULL\n" \
|
" struct TemplateNode next = NULL\n" \
|
||||||
" } %p\n" \
|
" } %p\n" \
|
||||||
" } %p\n" \
|
" } %p\n" \
|
||||||
@@ -50,31 +47,29 @@
|
|||||||
" content = %p \"Loop content\"\n" \
|
" content = %p \"Loop content\"\n" \
|
||||||
" } %p\n" \
|
" } %p\n" \
|
||||||
" } %p,\n" \
|
" } %p,\n" \
|
||||||
" struct TemplateNode children = NULL,\n" \
|
|
||||||
" struct TemplateNode next = NULL\n" \
|
" struct TemplateNode next = NULL\n" \
|
||||||
" } %p\n" \
|
" } %p\n" \
|
||||||
" } %p\n" \
|
" } %p\n" \
|
||||||
" } %p,\n" \
|
" } %p,\n" \
|
||||||
" struct TemplateNode children = NULL,\n" \
|
|
||||||
" struct TemplateNode next = NULL\n" \
|
" struct TemplateNode next = NULL\n" \
|
||||||
"} %p\n"
|
"} %p\n"
|
||||||
|
|
||||||
|
|
||||||
static void test_template_node_to_debug_str(Arena *arena) {
|
static void test_template_node_to_debug_str(Arena *arena) {
|
||||||
TemplateNode *root = arena_alloc(arena, sizeof(TemplateNode));
|
TemplateNode *root = arena_alloc(arena, sizeof(TemplateNode));
|
||||||
root->type = TEMPLATE_NODE_TEXT;
|
*root = init_template_node(arena, TEMPLATE_NODE_TEXT);
|
||||||
root->value.text.content = arena_strncpy(arena, "Hello", 5);
|
root->value->text.content = arena_strncpy(arena, "Hello", 5);
|
||||||
|
|
||||||
root->next = arena_alloc(arena, sizeof(TemplateNode));
|
root->next = arena_alloc(arena, sizeof(TemplateNode));
|
||||||
root->next->type = TEMPLATE_NODE_INTERPOLATE;
|
*root->next = init_template_node(arena, TEMPLATE_NODE_INTERPOLATE);
|
||||||
root->next->value.interpolate.key = arena_strncpy(arena, "name", 4);
|
root->next->value->interpolate.key = arena_strncpy(arena, "name", 4);
|
||||||
|
|
||||||
root->next->next = arena_alloc(arena, sizeof(TemplateNode));
|
root->next->next = arena_alloc(arena, sizeof(TemplateNode));
|
||||||
root->next->next->type = TEMPLATE_NODE_TEXT;
|
*root->next->next = init_template_node(arena, TEMPLATE_NODE_TEXT);
|
||||||
root->next->next->value.text.content = arena_strncpy(arena, "!", 1);
|
root->next->next->value->text.content = arena_strncpy(arena, "!", 1);
|
||||||
|
|
||||||
char *debug_str = debug_to_pretty_str(arena, TEMPLATE_NODE_TO_DEBUG_STR(arena, "root", root));
|
char *debug_str = debug_to_pretty_str(arena, TEMPLATE_NODE_TO_DEBUG_STR(arena, "root", root));
|
||||||
raise_log("debug_str: \n%s", debug_str);
|
raise_notice("debug_str: \n%s", debug_str);
|
||||||
|
|
||||||
{ // some debug output
|
{ // some debug output
|
||||||
Arena *debug_arena = DISPOSABLE_ARENA;
|
Arena *debug_arena = DISPOSABLE_ARENA;
|
||||||
@@ -85,21 +80,21 @@ static void test_template_node_to_debug_str(Arena *arena) {
|
|||||||
|
|
||||||
char *expected_debug_str = arena_alloc(arena, MEM_KiB);
|
char *expected_debug_str = arena_alloc(arena, MEM_KiB);
|
||||||
sprintf(expected_debug_str, TEST_TEMPLATE_NODE_TO_DEBUG_STR,
|
sprintf(expected_debug_str, TEST_TEMPLATE_NODE_TO_DEBUG_STR,
|
||||||
(void*)root->value.text.content,
|
(void*)root->value->text.content,
|
||||||
(void*)&root->value.text,
|
(void*)&root->value->text,
|
||||||
(void*)&root->value,
|
(void*)root->value,
|
||||||
(void*)root->next->value.interpolate.key,
|
(void*)root->next->value->interpolate.key,
|
||||||
(void*)&root->next->value.interpolate,
|
(void*)&root->next->value->interpolate,
|
||||||
(void*)&root->next->value,
|
(void*)root->next->value,
|
||||||
(void*)root->next->next->value.text.content,
|
(void*)root->next->next->value->text.content,
|
||||||
(void*)&root->next->next->value.text,
|
(void*)&root->next->next->value->text,
|
||||||
(void*)&root->next->next->value,
|
(void*)root->next->next->value,
|
||||||
(void*)root->next->next,
|
(void*)root->next->next,
|
||||||
(void*)root->next,
|
(void*)root->next,
|
||||||
(void*)root
|
(void*)root
|
||||||
);
|
);
|
||||||
|
|
||||||
raise_log("expected_debug_str: \n%s", expected_debug_str);
|
raise_notice("expected_debug_str: \n%s", expected_debug_str);
|
||||||
|
|
||||||
assert(strcmp(debug_str, expected_debug_str) == 0);
|
assert(strcmp(debug_str, expected_debug_str) == 0);
|
||||||
}
|
}
|
||||||
@@ -107,20 +102,14 @@ static void test_template_node_to_debug_str(Arena *arena) {
|
|||||||
static void test_template_section_node_to_debug_str(Arena *arena) {
|
static void test_template_section_node_to_debug_str(Arena *arena) {
|
||||||
// Create a section node with a child text node
|
// Create a section node with a child text node
|
||||||
TemplateNode *root = arena_alloc(arena, sizeof(TemplateNode));
|
TemplateNode *root = arena_alloc(arena, sizeof(TemplateNode));
|
||||||
root->type = TEMPLATE_NODE_SECTION;
|
*root = init_template_node(arena, TEMPLATE_NODE_SECTION);
|
||||||
root->value.section.iterator = arena_strncpy(arena, "item", 4);
|
root->value->section.iterator = arena_strncpy(arena, "item", 4);
|
||||||
root->value.section.collection = arena_strncpy(arena, "items", 5);
|
root->value->section.collection = arena_strncpy(arena, "items", 5);
|
||||||
|
|
||||||
// Create a body node (child of section)
|
// Create a body node (child of section)
|
||||||
root->value.section.body = arena_alloc(arena, sizeof(TemplateNode));
|
root->value->section.body = arena_alloc(arena, sizeof(TemplateNode));
|
||||||
root->value.section.body->type = TEMPLATE_NODE_TEXT;
|
*root->value->section.body = init_template_node(arena, TEMPLATE_NODE_TEXT);
|
||||||
root->value.section.body->value.text.content = arena_strncpy(arena, "Loop content", 12);
|
root->value->section.body->value->text.content = arena_strncpy(arena, "Loop content", 12);
|
||||||
|
|
||||||
// SAFETY(yukkop): if any of these are not NULL, the node will be corrupted
|
|
||||||
root->value.section.body->next = NULL;
|
|
||||||
root->value.section.body->children = NULL;
|
|
||||||
root->next = NULL;
|
|
||||||
root->children = NULL;
|
|
||||||
|
|
||||||
char *debug_str = debug_to_pretty_str(arena, TEMPLATE_NODE_TO_DEBUG_STR(arena, "root", root));
|
char *debug_str = debug_to_pretty_str(arena, TEMPLATE_NODE_TO_DEBUG_STR(arena, "root", root));
|
||||||
raise_log("debug_str: \n%s", debug_str);
|
raise_log("debug_str: \n%s", debug_str);
|
||||||
@@ -134,14 +123,14 @@ static void test_template_section_node_to_debug_str(Arena *arena) {
|
|||||||
|
|
||||||
char *expected_debug_str = arena_alloc(arena, MEM_KiB);
|
char *expected_debug_str = arena_alloc(arena, MEM_KiB);
|
||||||
sprintf(expected_debug_str, TEST_TEMPLATE_SECTION_NODE_TO_DEBUG_STR,
|
sprintf(expected_debug_str, TEST_TEMPLATE_SECTION_NODE_TO_DEBUG_STR,
|
||||||
(void*)root->value.section.iterator,
|
(void*)root->value->section.iterator,
|
||||||
(void*)root->value.section.collection,
|
(void*)root->value->section.collection,
|
||||||
(void*)root->value.section.body->value.text.content,
|
(void*)root->value->section.body->value->text.content,
|
||||||
(void*)&root->value.section.body->value.text,
|
(void*)&root->value->section.body->value->text,
|
||||||
(void*)&root->value.section.body->value,
|
(void*)root->value->section.body->value,
|
||||||
(void*)root->value.section.body,
|
(void*)root->value->section.body,
|
||||||
(void*)&root->value.section,
|
(void*)&root->value->section,
|
||||||
(void*)&root->value,
|
(void*)root->value,
|
||||||
(void*)root
|
(void*)root
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -150,6 +139,47 @@ static void test_template_section_node_to_debug_str(Arena *arena) {
|
|||||||
assert(strcmp(debug_str, expected_debug_str) == 0);
|
assert(strcmp(debug_str, expected_debug_str) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void simplest_test_template_parse(Arena *arena, TemplateConfig *config) {
|
||||||
|
const char *template_str = "{{ name }}";
|
||||||
|
|
||||||
|
TemplateResult template_result = template_parse(arena, &template_str, config);
|
||||||
|
|
||||||
|
if (IS_RESULT_ERROR(template_result)) {
|
||||||
|
raise_exception("template_parse failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateNode node = RESULT_SOME_VALUE(template_result);
|
||||||
|
|
||||||
|
{ // some debug output
|
||||||
|
Arena *debug_arena = DISPOSABLE_ARENA;
|
||||||
|
const char *json_str = TEMPLATE_NODE_TO_JSON_STR(debug_arena, &node);
|
||||||
|
Json *json = json_parse(debug_arena, &json_str);
|
||||||
|
raise_notice("json_str: \n%s", JSON_TO_PRETTY_STR(debug_arena, json));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void simplest_interpolation_test_template_parse(Arena *arena, TemplateConfig *config) {
|
||||||
|
const char *template_str = "{% name %} {% name2 %}";
|
||||||
|
|
||||||
|
TemplateResult template_result = template_parse(arena, &template_str, config);
|
||||||
|
|
||||||
|
if (IS_RESULT_ERROR(template_result)) {
|
||||||
|
raise_exception("template_parse failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateNode node = RESULT_SOME_VALUE(template_result);
|
||||||
|
|
||||||
|
{ // some debug output
|
||||||
|
Arena *debug_arena = DISPOSABLE_ARENA;
|
||||||
|
const char *json_str = TEMPLATE_NODE_TO_JSON_STR(debug_arena, &node);
|
||||||
|
Json *json = json_parse(debug_arena, &json_str);
|
||||||
|
raise_log("json_str: \n%s", json_str);
|
||||||
|
raise_notice("json_str: \n%s", JSON_TO_PRETTY_STR(debug_arena, json));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
printf("%sRunning %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
|
printf("%sRunning %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
|
||||||
debug_color_mode = COLOR_MODE_DISABLE;
|
debug_color_mode = COLOR_MODE_DISABLE;
|
||||||
@@ -157,8 +187,6 @@ int main(void) {
|
|||||||
|
|
||||||
Arena arena = arena_init(ARENA_SIZE);
|
Arena arena = arena_init(ARENA_SIZE);
|
||||||
|
|
||||||
//TemplateConfig config = template_default_config();
|
|
||||||
|
|
||||||
printf("%sRunning template parser tests...%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
printf("%sRunning template parser tests...%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
||||||
|
|
||||||
test_template_node_to_debug_str(&arena);
|
test_template_node_to_debug_str(&arena);
|
||||||
@@ -169,11 +197,19 @@ int main(void) {
|
|||||||
printf("%sTest 1: template_section_node_to_debug_str passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
printf("%sTest 1: template_section_node_to_debug_str passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
||||||
arena_reset(&arena);
|
arena_reset(&arena);
|
||||||
|
|
||||||
//test_template_parse(&arena, &config);
|
Arena config_arena = arena_init(ARENA_SIZE);
|
||||||
//printf("%sTest 1: template_parse passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
TemplateConfig config = template_default_config(&config_arena);
|
||||||
//arena_reset(&arena);
|
|
||||||
|
simplest_test_template_parse(&arena, &config);
|
||||||
|
printf("%sTest 2: simplest_test_template_parse passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
||||||
|
arena_reset(&arena);
|
||||||
|
|
||||||
|
simplest_interpolation_test_template_parse(&arena, &config);
|
||||||
|
printf("%sTest 3: simplest_interpolation_test_template_parse passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
||||||
|
arena_reset(&arena);
|
||||||
|
|
||||||
logger_free();
|
logger_free();
|
||||||
|
arena_free(&config_arena);
|
||||||
arena_free(&arena);
|
arena_free(&arena);
|
||||||
printf("%sall tests passed %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
|
printf("%sall tests passed %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user