diff --git a/package/c/hectic/hectic.c b/package/c/hectic/hectic.c index 5fe1662..3be4781 100644 --- a/package/c/hectic/hectic.c +++ b/package/c/hectic/hectic.c @@ -1390,6 +1390,7 @@ static Json *json_parse_value__(POSITION_INFO_DECLARATION, const char **s, Arena return NULL; } +// FIXME(yukkop): **s changes in the function. Need to fix. Json *json_parse__(POSITION_INFO_DECLARATION, Arena *arena, const char **s) { // Function entry logging with DEBUG level 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"); 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) { ptr += sprintf(ptr, "%s", child_str); } else { @@ -2390,7 +2391,7 @@ TemplateNode new_template_node__(TemplateNodeType type, TemplateValue *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, bool inner_parse); 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"); @@ -2443,10 +2444,9 @@ TemplateResult template_parse_section__(POSITION_INFO_DECLARATION, Arena *arena, const char *iterator_start = *s; while (**s != '\0') { - 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->data, "Unexpected section end", TEMPLATE_ERROR_UNEXPECTED_SECTION_END); + if (isspace(**s) || strncmp(*s, config->Syntax.Section.source->data, config->Syntax.Section.source->len) == 0) break; 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)++; } @@ -2455,27 +2455,37 @@ TemplateResult template_parse_section__(POSITION_INFO_DECLARATION, Arena *arena, // Find the collection name *s = skip_whitespace(*s); + *s += config->Syntax.Section.source->len; + *s = skip_whitespace(*s); const char *collection_start = *s; 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.open->data, "Nested tag in section iterator", TEMPLATE_ERROR_NESTED_SECTION_ITERATOR); - (*s)++; } size_t collection_len = *s - collection_start; 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 - 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) { return body_result; } 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; return result; @@ -2497,15 +2507,20 @@ TemplateResult template_parse_include__(POSITION_INFO_DECLARATION, Arena *arena, const char *include_start = *s; 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); - (*s)++; } size_t include_len = *s - include_start; 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; return result; @@ -2518,6 +2533,7 @@ TemplateResult template_parse_execute__(POSITION_INFO_DECLARATION, Arena *arena, const char **s = s_ptr; + // Skip to the content of the execute *s += config->Syntax.Braces.open->len; *s = skip_whitespace(*s); *s += config->Syntax.Execute.invoke->len; @@ -2525,7 +2541,9 @@ TemplateResult template_parse_execute__(POSITION_INFO_DECLARATION, Arena *arena, *s = skip_whitespace(*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); (*s)++; } @@ -2533,12 +2551,17 @@ TemplateResult template_parse_execute__(POSITION_INFO_DECLARATION, Arena *arena, size_t code_len = *s - code_start; 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; 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"); 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; + 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 (start != *s) { 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); char *body_str = template_node_to_json_str__(POSITION_INFO, arena, node->value->section.body, depth + 1); if (body_str) { - APPEND(",\"body\":%s", body_str); + APPEND(",\"body\":[%s]", body_str); } break; case TEMPLATE_NODE_INTERPOLATE: diff --git a/package/c/hectic/hectic.h b/package/c/hectic/hectic.h index 994d765..2fd662b 100644 --- a/package/c/hectic/hectic.h +++ b/package/c/hectic/hectic.h @@ -97,22 +97,24 @@ void set_output_color_mode(ColorMode mode); typedef enum { HECTIC_ERROR_NONE = 0, - TEMPLATE_ERROR_NONE = 985567, - TEMPLATE_ERROR_UNKNOWN_TAG = 985568, - TEMPLATE_ERROR_NESTED_INTERPOLATION = 985569, - TEMPLATE_ERROR_NESTED_SECTION_ITERATOR = 985570, - TEMPLATE_ERROR_UNEXPECTED_SECTION_END = 985571, - TEMPLATE_ERROR_NESTED_INCLUDE = 985572, - TEMPLATE_ERROR_NESTED_EXECUTE = 985573, - TEMPLATE_ERROR_INVALID_CONFIG = 985574, - TEMPLATE_ERROR_OUT_OF_MEMORY = 985575, - LOGGER_ERROR_INVALID_RULES_STRING = 985576, - LOGGER_ERROR_OUT_OF_MEMORY = 985577, - DEBUG_TO_JSON_PARSE_NO_EQUAL_SIGN_ERROR = 985578, - DEBUG_TO_JSON_PARSE_NO_STRUCT_NAME_ERROR = 985579, - DEBUG_TO_JSON_PARSE_LEFT_OPERAND_ERROR = 985580, - DEBUG_TO_JSON_PARSE_NO_START_ERROR = 985581, - DEBUG_TO_JSON_PARSE_NO_END_ERROR = 985582, + TEMPLATE_ERROR_NONE = 900000, + TEMPLATE_ERROR_UNKNOWN_TAG = 900001, + TEMPLATE_ERROR_NESTED_INTERPOLATION = 900002, + TEMPLATE_ERROR_NESTED_SECTION_ITERATOR = 900003, + TEMPLATE_ERROR_UNEXPECTED_SECTION_END = 900004, + TEMPLATE_ERROR_NESTED_INCLUDE = 900005, + TEMPLATE_ERROR_NESTED_EXECUTE = 900006, + TEMPLATE_ERROR_INVALID_CONFIG = 900007, + TEMPLATE_ERROR_OUT_OF_MEMORY = 900008, + TEMPLATE_ERROR_UNEXPECTED_INCLUDE_END = 900009, + TEMPLATE_ERROR_UNEXPECTED_EXECUTE_END = 900010, + LOGGER_ERROR_INVALID_RULES_STRING = 800001, + LOGGER_ERROR_OUT_OF_MEMORY = 800002, + DEBUG_TO_JSON_PARSE_NO_EQUAL_SIGN_ERROR = 700003, + DEBUG_TO_JSON_PARSE_NO_STRUCT_NAME_ERROR = 700004, + 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; // Define color macros based on output type @@ -814,7 +816,7 @@ struct 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); @@ -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); -#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) diff --git a/package/c/hectic/test/06-templater.c b/package/c/hectic/test/06-templater.c index 3aa6e62..a2a29a8 100755 --- a/package/c/hectic/test/06-templater.c +++ b/package/c/hectic/test/06-templater.c @@ -151,12 +151,24 @@ static void simplest_test_template_parse(Arena *arena, TemplateConfig *config) { TemplateNode node = RESULT_SOME_VALUE(template_result); + char *result_str; { // 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)); + 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) { @@ -171,12 +183,62 @@ static void simplest_interpolation_test_template_parse(Arena *arena, TemplateCon TemplateNode node = RESULT_SOME_VALUE(template_result); + char *result_str; { // 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)); + 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)); 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(); arena_free(&config_arena); arena_free(&arena);