fix: hectic C: template parse sections

This commit is contained in:
2025-05-04 19:33:52 +00:00
parent 9b045b71f9
commit 4db5cc171b
3 changed files with 129 additions and 32 deletions

View File

@@ -1390,6 +1390,7 @@ static Json *json_parse_value__(POSITION_INFO_DECLARATION, const char **s, Arena
return NULL; return NULL;
} }
// FIXME(yukkop): **s changes in the function. Need to fix.
Json *json_parse__(POSITION_INFO_DECLARATION, Arena *arena, const char **s) { Json *json_parse__(POSITION_INFO_DECLARATION, Arena *arena, const char **s) {
// Function entry logging with DEBUG level // Function entry logging with DEBUG level
raise_message(LOG_LEVEL_DEBUG, POSITION_INFO, raise_message(LOG_LEVEL_DEBUG, POSITION_INFO,
@@ -1508,7 +1509,7 @@ char *json_to_string_with_opts__(POSITION_INFO_DECLARATION, Arena *arena, const
"FORMAT: Processing JSON array elements"); "FORMAT: Processing JSON array elements");
while (child) { while (child) {
char *child_str = json_to_string_with_opts__(file, func, line, arena, child, raw); char *child_str = json_to_string_with_opts__(POSITION_INFO, arena, child, raw);
if (child_str) { if (child_str) {
ptr += sprintf(ptr, "%s", child_str); ptr += sprintf(ptr, "%s", child_str);
} else { } else {
@@ -2390,7 +2391,7 @@ TemplateNode new_template_node__(TemplateNodeType type, TemplateValue *value) {
return node; 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, bool inner_parse);
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");
@@ -2443,10 +2444,9 @@ TemplateResult template_parse_section__(POSITION_INFO_DECLARATION, Arena *arena,
const char *iterator_start = *s; const char *iterator_start = *s;
while (**s != '\0') { while (**s != '\0') {
if (**s == ' ' || **s == '\n' || **s == '\t' || strncmp(*s, config->Syntax.Section.source->data, config->Syntax.Section.source->len) == 0) break; if (isspace(**s) || strncmp(*s, config->Syntax.Section.source->data, config->Syntax.Section.source->len) == 0) break;
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.close->data, "Unexpected section end", TEMPLATE_ERROR_UNEXPECTED_SECTION_END);
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open->data, "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);
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.close->data, "Unexpected section end", TEMPLATE_ERROR_UNEXPECTED_SECTION_END);
(*s)++; (*s)++;
} }
@@ -2455,27 +2455,37 @@ TemplateResult template_parse_section__(POSITION_INFO_DECLARATION, Arena *arena,
// Find the collection name // Find the collection name
*s = skip_whitespace(*s); *s = skip_whitespace(*s);
*s += config->Syntax.Section.source->len;
*s = skip_whitespace(*s);
const char *collection_start = *s; const char *collection_start = *s;
while (**s != '\0') { while (**s != '\0') {
if (**s == ' ' || **s == '\n' || **s == '\t' || strncmp(*s, config->Syntax.Section.begin->data, config->Syntax.Section.begin->len) == 0) break; if (isspace(**s) || strncmp(*s, config->Syntax.Section.begin->data, config->Syntax.Section.begin->len) == 0) break;
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open->data, "Nested tag in section collection", TEMPLATE_ERROR_NESTED_SECTION_ITERATOR);
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.close->data, "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->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);
// Skip to the body
*s = skip_whitespace(*s);
// 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, true);
if (body_result.type == RESULT_ERROR) { if (body_result.type == RESULT_ERROR) {
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;
// Skip to the end of the section
*s = skip_whitespace(*s);
if (strncmp(*s, config->Syntax.Braces.close->data, config->Syntax.Braces.close->len) != 0) {
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "PARSE: Expected section end");
return RESULT_ERROR(TemplateResult, TEMPLATE_ERROR_UNEXPECTED_SECTION_END, "Expected section end");
}
*s_ptr = *s + config->Syntax.Braces.close->len; *s_ptr = *s + config->Syntax.Braces.close->len;
return result; return result;
@@ -2497,15 +2507,20 @@ TemplateResult template_parse_include__(POSITION_INFO_DECLARATION, Arena *arena,
const char *include_start = *s; const char *include_start = *s;
while (**s != '\0') { while (**s != '\0') {
if (**s == ' ' || **s == '\n' || **s == '\t' || strncmp(*s, config->Syntax.Braces.close->data, config->Syntax.Braces.close->len) == 0) 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->data, "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);
// Skip to the end of the include
*s = skip_whitespace(*s);
if (strncmp(*s, config->Syntax.Braces.close->data, config->Syntax.Braces.close->len) != 0) {
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "PARSE: Expected include end");
return RESULT_ERROR(TemplateResult, TEMPLATE_ERROR_UNEXPECTED_INCLUDE_END, "Expected include end");
}
*s_ptr = *s + config->Syntax.Braces.close->len; *s_ptr = *s + config->Syntax.Braces.close->len;
return result; return result;
@@ -2518,6 +2533,7 @@ TemplateResult template_parse_execute__(POSITION_INFO_DECLARATION, Arena *arena,
const char **s = s_ptr; const char **s = s_ptr;
// Skip to the content of the execute
*s += config->Syntax.Braces.open->len; *s += config->Syntax.Braces.open->len;
*s = skip_whitespace(*s); *s = skip_whitespace(*s);
*s += config->Syntax.Execute.invoke->len; *s += config->Syntax.Execute.invoke->len;
@@ -2525,7 +2541,9 @@ TemplateResult template_parse_execute__(POSITION_INFO_DECLARATION, Arena *arena,
*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->data, config->Syntax.Braces.close->len) == 0) { // Find the end of the code
while (**s != '\0') {
if (strncmp(*s, config->Syntax.Braces.close->data, config->Syntax.Braces.close->len) == 0) break;
TEMPLATE_ASSERT_SYNTAX(config->Syntax.Braces.open->data, "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)++;
} }
@@ -2533,12 +2551,17 @@ TemplateResult template_parse_execute__(POSITION_INFO_DECLARATION, Arena *arena,
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);
// Skip to the end of the execute
if (strncmp(*s, config->Syntax.Braces.close->data, config->Syntax.Braces.close->len) != 0) {
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "PARSE: Expected execute end");
return RESULT_ERROR(TemplateResult, TEMPLATE_ERROR_UNEXPECTED_EXECUTE_END, "Expected execute end");
}
*s_ptr = *s + config->Syntax.Braces.close->len; *s_ptr = *s + config->Syntax.Braces.close->len;
return result; return result;
} }
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, bool inner_parse) {
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Iteration start"); raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Iteration start");
if (!template_validate_config__(POSITION_INFO, config)) { if (!template_validate_config__(POSITION_INFO, config)) {
@@ -2561,7 +2584,13 @@ TemplateResult template_parse__(POSITION_INFO_DECLARATION, Arena *arena, const c
int open_brace_len = config->Syntax.Braces.open->len; int open_brace_len = config->Syntax.Braces.open->len;
while (*s && **s != '\0') { while (*s && **s != '\0') {
// Check for closing brace if this is inner parse
if (inner_parse && strncmp(*s, config->Syntax.Braces.close->data, config->Syntax.Braces.close->len) == 0) {
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "PARSE: Found closing brace in inner parse");
break;
}
if (strncmp(*s, config->Syntax.Braces.open->data, open_brace_len) == 0) { if (strncmp(*s, config->Syntax.Braces.open->data, open_brace_len) == 0) {
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));
@@ -2796,7 +2825,7 @@ char *template_node_to_json_str__(POSITION_INFO_DECLARATION, Arena *arena, const
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:

View File

@@ -97,22 +97,24 @@ void set_output_color_mode(ColorMode mode);
typedef enum { typedef enum {
HECTIC_ERROR_NONE = 0, HECTIC_ERROR_NONE = 0,
TEMPLATE_ERROR_NONE = 985567, TEMPLATE_ERROR_NONE = 900000,
TEMPLATE_ERROR_UNKNOWN_TAG = 985568, TEMPLATE_ERROR_UNKNOWN_TAG = 900001,
TEMPLATE_ERROR_NESTED_INTERPOLATION = 985569, TEMPLATE_ERROR_NESTED_INTERPOLATION = 900002,
TEMPLATE_ERROR_NESTED_SECTION_ITERATOR = 985570, TEMPLATE_ERROR_NESTED_SECTION_ITERATOR = 900003,
TEMPLATE_ERROR_UNEXPECTED_SECTION_END = 985571, TEMPLATE_ERROR_UNEXPECTED_SECTION_END = 900004,
TEMPLATE_ERROR_NESTED_INCLUDE = 985572, TEMPLATE_ERROR_NESTED_INCLUDE = 900005,
TEMPLATE_ERROR_NESTED_EXECUTE = 985573, TEMPLATE_ERROR_NESTED_EXECUTE = 900006,
TEMPLATE_ERROR_INVALID_CONFIG = 985574, TEMPLATE_ERROR_INVALID_CONFIG = 900007,
TEMPLATE_ERROR_OUT_OF_MEMORY = 985575, TEMPLATE_ERROR_OUT_OF_MEMORY = 900008,
LOGGER_ERROR_INVALID_RULES_STRING = 985576, TEMPLATE_ERROR_UNEXPECTED_INCLUDE_END = 900009,
LOGGER_ERROR_OUT_OF_MEMORY = 985577, TEMPLATE_ERROR_UNEXPECTED_EXECUTE_END = 900010,
DEBUG_TO_JSON_PARSE_NO_EQUAL_SIGN_ERROR = 985578, LOGGER_ERROR_INVALID_RULES_STRING = 800001,
DEBUG_TO_JSON_PARSE_NO_STRUCT_NAME_ERROR = 985579, LOGGER_ERROR_OUT_OF_MEMORY = 800002,
DEBUG_TO_JSON_PARSE_LEFT_OPERAND_ERROR = 985580, DEBUG_TO_JSON_PARSE_NO_EQUAL_SIGN_ERROR = 700003,
DEBUG_TO_JSON_PARSE_NO_START_ERROR = 985581, DEBUG_TO_JSON_PARSE_NO_STRUCT_NAME_ERROR = 700004,
DEBUG_TO_JSON_PARSE_NO_END_ERROR = 985582, DEBUG_TO_JSON_PARSE_LEFT_OPERAND_ERROR = 700005,
DEBUG_TO_JSON_PARSE_NO_START_ERROR = 700006,
DEBUG_TO_JSON_PARSE_NO_END_ERROR = 700007,
} HecticErrorCode; } HecticErrorCode;
// Define color macros based on output type // Define color macros based on output type
@@ -814,7 +816,7 @@ struct TemplateNode {
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, bool inner_parse);
TemplateConfig template_default_config__(const char *file, const char *func, int line, Arena *arena); TemplateConfig template_default_config__(const char *file, const char *func, int line, Arena *arena);
@@ -822,7 +824,7 @@ char *template_node_to_debug_str__(const char *file, const char *func, int line,
char *template_node_to_json_str__(const char *file, const char *func, int line, Arena *arena, const TemplateNode *node, int depth); char *template_node_to_json_str__(const char *file, const char *func, int line, Arena *arena, const TemplateNode *node, int depth);
#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, false)
#define template_default_config(arena) template_default_config__(__FILE__, __func__, __LINE__, arena) #define template_default_config(arena) template_default_config__(__FILE__, __func__, __LINE__, arena)

View File

@@ -151,12 +151,24 @@ static void simplest_test_template_parse(Arena *arena, TemplateConfig *config) {
TemplateNode node = RESULT_SOME_VALUE(template_result); TemplateNode node = RESULT_SOME_VALUE(template_result);
char *result_str;
{ // some debug output { // some debug output
Arena *debug_arena = DISPOSABLE_ARENA; Arena *debug_arena = DISPOSABLE_ARENA;
const char *json_str = TEMPLATE_NODE_TO_JSON_STR(debug_arena, &node); const char *json_str = TEMPLATE_NODE_TO_JSON_STR(debug_arena, &node);
Json *json = json_parse(debug_arena, &json_str); Json *json = json_parse(debug_arena, &json_str);
raise_notice("json_str: \n%s", JSON_TO_PRETTY_STR(debug_arena, json)); raise_notice("json_str: \n%s", JSON_TO_PRETTY_STR(debug_arena, json));
result_str = arena_strdup(arena, JSON_TO_PRETTY_STR(debug_arena, json));
} }
assert(strcmp(result_str,
"[\n"
" {\n"
" \"type\": \"TEXT\",\n"
" \"content\": {\n"
" \"content\": \"{{ name }}\"\n"
" }\n"
" }\n"
"]") == 0);
} }
static void simplest_interpolation_test_template_parse(Arena *arena, TemplateConfig *config) { static void simplest_interpolation_test_template_parse(Arena *arena, TemplateConfig *config) {
@@ -171,12 +183,62 @@ static void simplest_interpolation_test_template_parse(Arena *arena, TemplateCon
TemplateNode node = RESULT_SOME_VALUE(template_result); TemplateNode node = RESULT_SOME_VALUE(template_result);
char *result_str;
{ // some debug output { // some debug output
Arena *debug_arena = DISPOSABLE_ARENA; Arena *debug_arena = DISPOSABLE_ARENA;
const char *json_str = TEMPLATE_NODE_TO_JSON_STR(debug_arena, &node); const char *json_str = TEMPLATE_NODE_TO_JSON_STR(debug_arena, &node);
Json *json = json_parse(debug_arena, &json_str); Json *json = json_parse(debug_arena, &json_str);
raise_log("json_str: \n%s", json_str); raise_log("json_str: \n%s", json_str);
raise_notice("json_str: \n%s", JSON_TO_PRETTY_STR(debug_arena, json)); raise_notice("json_str: \n%s", JSON_TO_PRETTY_STR(debug_arena, json));
result_str = arena_strdup(arena, JSON_TO_PRETTY_STR(debug_arena, json));
}
assert(strcmp(result_str,
"[\n"
" {\n"
" \"type\": \"INTERPOLATE\",\n"
" \"content\": {\n"
" \"key\": \"name\"\n"
" }\n"
" },\n"
" {\n"
" \"type\": \"TEXT\",\n"
" \"content\": {\n"
" \"content\": \" \"\n"
" }\n"
" },\n"
" {\n"
" \"type\": \"INTERPOLATE\",\n"
" \"content\": {\n"
" \"key\": \"name2\"\n"
" }\n"
" }\n"
"]") == 0);
}
static void simplest_separator_test_template_parse(Arena *arena, TemplateConfig *config) {
const char *template_str = "{%"
" for item in items"
" {% name %} {% item.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);
raise_log("json_str: \n%s", json_str);
Json *json = json_parse(debug_arena, &json_str);
raise_notice("json_str: \n%s", JSON_TO_PRETTY_STR(debug_arena, json));
} }
} }
@@ -208,6 +270,10 @@ int main(void) {
printf("%sTest 3: simplest_interpolation_test_template_parse passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET)); printf("%sTest 3: simplest_interpolation_test_template_parse passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
arena_reset(&arena); arena_reset(&arena);
simplest_separator_test_template_parse(&arena, &config);
printf("%sTest 4: simplest_separator_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(&config_arena);
arena_free(&arena); arena_free(&arena);