Mercurial > thymian
diff templates/templater.cpp @ 0:a4671277546c tip
created the repository for the thymian project
| author | ferencd |
|---|---|
| date | Tue, 17 Aug 2021 11:19:54 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/templater.cpp Tue Aug 17 11:19:54 2021 +0200 @@ -0,0 +1,1144 @@ +#include "templater.h" +#include "dictionary.h" + +#ifdef PYTHON_SCRIPTING +#include "python_runner.h" +#endif + +#include <algorithm> +#include <filesystem> + +#include <log.h> +#include <cwctype> + + +void templater_base::resolve_all_includes(std::string& templatized, bool do_replace) +{ + size_t inc_pos = templatized.find(INCLUDE_TAG); + while(inc_pos != std::string::npos) + { + templatized = resolve_includes(templatized, inc_pos, do_replace); + + // see if we have pulled in some extra vars + if(do_replace) + { + templatized = stringholder(templatized).templatize(kps).get(); + } + + inc_pos = templatized.find(INCLUDE_TAG); + } +} + +std::string templater_base::get(const std::string &template_name) +{ + // Getting the content + std::string content = template_warehouse::instance().getTemplateContent(template_name); + + // Resolve the #define's. This will update the extra_kp's + std::string content_without_vars = resolve_defines(content); + + // resolve the initializer scripts, since they may modify the kps + std::string scripts_resolved = resolve_scripts(content_without_vars, INIT_SCRIPT_TAG); + + // resolve all the top level template arguments + std::string templatized = stringholder(scripts_resolved).templatize(kps).get(); + + // then search for all the {#include 's and: templatize those with the given variable: Value pairs + // and include them here + bool done = false; + while(!done) + { + // search for structure definitions, there can be more than one + size_t struct_pos = templatized.find(STRUCT_TAG); + while(struct_pos != std::string::npos) + { + templatized = resolve_structure_declaration(struct_pos, templatized); + struct_pos = templatized.find(STRUCT_TAG); + } + + // search for input data definition hints + size_t parameters_pos = templatized.find(PARAMETERS_TAG); + if(parameters_pos != std::string::npos) + { + templatized = resolve_parameters(parameters_pos, templatized); + } + + // search for the "include"'s + resolve_all_includes(templatized); + + // now search for the "if"'s. Remember, ifs cannot have ifs in their body + size_t if_pos = templatized.find(IF_TAG); + if(if_pos != std::string::npos) + { + while(if_pos != std::string::npos) + { + templatized = resolve_ifs(if_pos, templatized); + if_pos = templatized.find(IF_TAG); + } + } + else + { + done = true; + } + } + + return templatized; +} + +std::string templater_base::extract_identifier_word(const std::string& input, size_t& i, std::vector<char> separators, + std::set<char> extra_allowed_chars, char&& c) +{ + skip_whitespace(input, i); + + std::string result = ""; + while(i < input.length() && (input.at(i) == '_' || + extra_allowed_chars.count(input.at(i)) || + isalnum(input.at(i))) ) + { + if(separators.empty()) + { + result += input.at(i); + i++; + } + else + { + if(find(separators.begin(), separators.end(), input.at(i)) == separators.end()) + { + result += input.at(i); + i++; + } + else + { + if(c != 0) + { + c = input[i]; + } + i ++; // skip the actual separator + break; + } + } + } + + skip_whitespace(input, i); + if(!separators.empty()) + { + if(find(separators.begin(), separators.end(), input.at(i)) != separators.end()) + { + if(c != 0) + { + c = input[i]; + } + i++; // this characater is a separator. Skip it. And the space after + skip_whitespace(input, i); + } + } + + return result; +} + +bool templater_base::check_opening_parenthesis(const std::string& input, size_t& i) +{ + bool opening_parentheses = false; + // skip the parenthesis if any + if(i < input.length() && input.at(i) == '(') + { + i ++; + skip_whitespace(input, i); + opening_parentheses = true; + } + + return opening_parentheses; +} + +bool templater_base::check_closing_comment(const std::string& templatized, size_t& i, std::size_t& include_tag_end_position) +{ + if(i + 3 < templatized.length()) + { + if(templatized[i+1] == '-' && templatized[i+2] == '-' && templatized[i + 3] == '>') + { + include_tag_end_position = i + 4; + return true; + } + } + return false; +} + +std::string templater_base::get_error() const +{ + return error; +} + +std::vector<std::string> templater_base::variables(bool resolve_includes_too) +{ + std::vector<std::string> result; + + std::string content = template_warehouse::instance().getTemplateContent(name()); + std::string::size_type start_pos = 0; + + if(resolve_includes_too) + { + resolve_all_includes(content, false); + } + + while((start_pos = content.find(TEMPLATE_VARIABLE_START_DELIMITER, start_pos)) != std::string::npos) + { + std::string current_varname = ""; + start_pos += TEMPLATE_VARIABLE_START_DELIMITER.length(); + while(start_pos < content.length() && content[start_pos] != '}') + { + current_varname += content[start_pos++]; + } + if(start_pos == content.length()) + { + set_error("Unterminated variable name"); + return result; + } + result.push_back(current_varname); + start_pos ++; // to skip the closing '}' + } + + return result; +} + +std::string templater_base::resolve_includes(std::string templatized, size_t inc_pos, bool do_variable_replacement) +{ + std::map<std::string, std::string> local_pairs; + std::size_t include_tag_end_position = std::string::npos; + std::size_t include_tag_start_position = inc_pos; + + inc_pos += INCLUDE_TAG.length(); + + std::string inc_template = extract_identifier_word(templatized, inc_pos); + bool opening_parentheses = check_opening_parenthesis(templatized, inc_pos); + size_t i = inc_pos; + bool closing_comment_found = false; + + while(i < templatized.length() && ! closing_comment_found) + { + std::string var_name = extract_identifier_word(templatized, i, {':','='}); + if(i == templatized.length() || var_name.empty()) + { + if(templatized[i] == '#' && !closing_comment_found) // This should be the closing comment + { + check_closing_comment(templatized, i, include_tag_end_position); + } + + break; + } + + // now read the variable value + std::string var_value = ""; + bool var_value_read = false; + while(!var_value_read && i < templatized.length()) + { + if(templatized[i] == '"') // string starts. Do not read the '"' + { + i++; // skip the '"' + + bool string_read = false; + while(i < templatized.length() && !string_read) + { + if(templatized[i] == '\\') // Skip the backslash from the escape sequence + { + i++; + } + + var_value += templatized[i++]; + + if(i == templatized.length()) + { + set_error("String literal is not finished"); + break; + } + + if(templatized[i] == '"' && templatized[i-1] != '\\') + { + string_read = true; + } + } + + if(i == templatized.length()) // invalid syntax + { + set_error("Invalid syntax, could not resolve an include"); + break; + } + + i++; // skip closing quote + } + else + { + // see if this is a closing parenthesis, but only if there was an opening one + if(opening_parentheses && templatized[i] == ')') + { + var_value_read = true; + + // now see if there is a closing comment. Everything after it will be ignored + while(i < templatized.length() && !closing_comment_found) + { + i ++; + skip_whitespace(templatized, i); + if(i < templatized.length() && templatized[i] == '#') // This should be the closing comment + { + closing_comment_found = check_closing_comment(templatized, i, include_tag_end_position); + } + } + } + + if(templatized[i] == '#' && !closing_comment_found) // This should be the closing comment + { + closing_comment_found = var_value_read = check_closing_comment(templatized, i, include_tag_end_position); + } + + if(templatized[i] == '{' && i<templatized.length() && templatized[i + 1] == '#') // The substitution of a variable with another one from this level + { + // Read in the entire variable and simply change the var_value to the one in our objects' map + i+=2; // skip {# + std::string upperlevel_var_name = extract_identifier_word(templatized, i, {'}'}); + + if(i == templatized.length() || upperlevel_var_name.empty()) + { + break; + } + + if(kps.count(upperlevel_var_name) != 0) + { + var_value += kps[upperlevel_var_name]; + } + } + + // and comma (or space) separatin the variables? + if(i < templatized.length() && (templatized[i] == ',' || iswspace(templatized[i])) ) + { + var_value_read = true; + i++; + skip_whitespace(templatized, i); + } + + // still not read the value? + if(i < templatized.length() && !var_value_read) + { + var_value += templatized[i++]; + } + } + } + + skip_whitespace(templatized, i); + + // now insert into local_pairs the keys and values + local_pairs.insert(make_pair(var_name, var_value)); + } + + // and now read in the template from inc_template + // and replace everything that is supposed to be replaced + std::string inc_content = template_warehouse::instance().getTemplateContent( inc_template ); + std::string inc_templatized = do_variable_replacement ? stringholder(inc_content).templatize(local_pairs).get() : inc_content; + + templatized.erase(include_tag_start_position, include_tag_end_position - include_tag_start_position); + templatized.insert(include_tag_start_position, inc_templatized); + return templatized; +} + +std::string templater_base::resolve_structure_declaration(size_t struct_pos, std::string templatized) +{ + std::size_t include_tag_start_position = struct_pos; + + struct_pos += STRUCT_TAG.length(); + + std::string struct_name = extract_identifier_word(templatized, struct_pos); + bool opening_parentheses = check_opening_parenthesis(templatized, struct_pos); + + add_structure_decl(struct_name, struct_name); + + // and now read in the structure members' names + size_t i = struct_pos; + bool closing_comment_found = false; + std::size_t include_tag_end_position = std::string::npos; + + while (i < templatized.length() && !closing_comment_found) + { + std::string member_name = ""; + while(i < templatized.length() && (isalnum(templatized[i]) || templatized[i] == '_')) + { + member_name += templatized[i++]; + } + if(member_name.empty()) // syntax error, just give back what came out + { + set_error("Invalid syntax, could not resolve a structure declaration"); + return templatized; + } + + skip_whitespace(templatized, i); + + // var_name now is the name of the structure's member, make it + // a default value + get_structure(struct_name)[member_name] = ""; + + // now this should point either to a space or a "," + i++; + + // se if we have a "," coming up + if(templatized[i] == ',') + { + i ++; + skip_whitespace(templatized, i); + } + + // see if this is a closing parenthesis, but only if there was an opening one + if(opening_parentheses && templatized[i] == ')') + { + + // now see if there is a closing comment. Everything after it will be ignored + while(i < templatized.length() && !closing_comment_found) + { + i ++; + skip_whitespace(templatized, i); + if(i < templatized.length() && templatized[i] == '#') // This should be the closing comment + { + closing_comment_found = check_closing_comment(templatized, i, include_tag_end_position); + } + } + } + + if(templatized[i] == '#' && !closing_comment_found) // This should be the closing comment + { + closing_comment_found = check_closing_comment(templatized, i, include_tag_end_position); + } + } + + templatized.erase(include_tag_start_position, include_tag_end_position - include_tag_start_position); + return templatized; +} + +templater_base &templater_base::templatize(const template_struct &s) +{ + precalculated = ""; + std::string templatized = get(); // will initialize the parameters + if(!m_parameters.empty()) + { + for(const auto &p : m_parameters) + { + if(p.first == s.name) + { + for(const auto &x : s.struct_members) + { + std::string fullname = s.name + "." + x.first; + kps.insert(make_pair(fullname, unafrog::utils::to_string(x.second))); + } + } + } + } + templatized = stringholder(templatized).templatize(kps).get(); + templatized = resolve_ifeqs(templatized); + templatized = resolve_scripts(templatized, SCRIPT_TAG); + precalculated = templatized; + + return *this; +} + +std::string templater_base::resolve_loops(std::string templatized, const template_vector_par &v) +{ + size_t loop_pos = templatized.find(LOOP_TAG); + while(loop_pos != std::string::npos) + { + // find the stuff between the iterate and end iterate tags + std::size_t loop_tag_end_position = std::string::npos; + std::size_t loop_tag_start_position = loop_pos; + + loop_pos += LOOP_TAG.length(); + + // see on what are we iterating through + std::string loop_target = extract_identifier_word(templatized, loop_pos); + + // there should be nothing more after this + if(!check_closing_comment(templatized, loop_pos, loop_tag_end_position)) + { + return templatized; + } + + // now find the end iterate tag + size_t end_loop_pos = templatized.find(ENDLOOP_TAG, loop_tag_end_position); + size_t save_endpos = end_loop_pos; + end_loop_pos += ENDLOOP_TAG.length(); + size_t endloop_endpos = std::string::npos; + + size_t temp = end_loop_pos; + + while(temp != std::string::npos) + { + size_t unused = temp; + + std::string endloop_target = extract_identifier_word(templatized, unused); + if(endloop_target == loop_target) + { + if(!check_closing_comment(templatized, unused, endloop_endpos)) + { + return templatized; + } + + break; + } + + temp = templatized.find(ENDLOOP_TAG, temp); + save_endpos = temp; + temp += ENDLOOP_TAG.length(); + } + + // now between loop_tag_end_position and save_endpos there is the strign we need + std::string loop_content = templatized.substr(loop_tag_end_position, save_endpos - loop_tag_end_position); + + std::string final_loop_content = ""; + + // and now insert a bunch of kps for the elements in v + if(!m_parameters.empty()) + { + size_t c = 0; + for(const auto &ts : v.value()) + { + c++; + for(const auto& p : m_parameters) + { + if(p.first == v.name()) + { + stringholder sh(loop_content); + + for(const auto &x : ts.struct_members) + { + //if(x.first == loop_target) + { + std::string fullname = v.name() + "." + x.first; + std::string fullname_to_replace_with = v.name() + "." + x.first + ":" + std::to_string(c); + + kps.insert(make_pair(fullname_to_replace_with, unafrog::utils::to_string(x.second))); + sh.replace_all(TEMPLATE_VARIABLE_START_DELIMITER + fullname +TEMPLATE_VARIABLE_END_DELIMITER, + TEMPLATE_VARIABLE_START_DELIMITER + fullname_to_replace_with + TEMPLATE_VARIABLE_END_DELIMITER); + } + } + + final_loop_content += sh.get(); + } + } + } + } + // now fetch out the substring (loop_tag_end_position, save_endpos - loop_tag_end_position) from templatized + // and replace with final_loop_content + + if(v.name() == loop_target) + { + std::string part1 = templatized.substr(0, loop_tag_end_position); + + part1.erase(loop_tag_start_position, loop_tag_end_position - loop_tag_start_position); + part1 += final_loop_content; + part1 += templatized.substr(endloop_endpos); + + templatized = part1; + loop_pos = templatized.find(LOOP_TAG); + } + else + { + loop_pos = templatized.find(LOOP_TAG, endloop_endpos); + } + } + + templatized = stringholder(templatized).templatize(kps).get(); + precalculated = templatized; + return templatized; +} + +std::string templater_base::resolve_ifeqs(std::string templatized) +{ + bool done = false; + while(!done) + { + size_t ifeq_pos = templatized.find(IFEQ_TAG); + if(ifeq_pos != std::string::npos) + { + while(ifeq_pos != std::string::npos) + { + templatized = resolve_ifeq(ifeq_pos, templatized); + ifeq_pos = templatized.find(IFEQ_TAG); + } + } + else + { + done = true; + } + } + return templatized; +} + +std::string templater_base::resolve_translations(std::string templatized, const std::string &target_language, bool generate_span, std::map<std::string, std::map<std::string, std::string> >& translations) +{ + bool done = false; + while(!done) + { + size_t ifeq_pos = templatized.find(TRANSLATE_TAG); + if(ifeq_pos != std::string::npos) + { + while(ifeq_pos != std::string::npos) + { + std::map<std::string, std::string> local_translations; + std::string span_id; + + templatized = resolve_translation(ifeq_pos, templatized, target_language, generate_span, span_id, local_translations); + + translations[span_id] = local_translations; + + ifeq_pos = templatized.find(TRANSLATE_TAG); + } + } + else + { + done = true; + } + } + return templatized; + +} + +static std::string span_name(std::string str) +{ + str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end()); + return "span_" + str; +} + +std::string templater_base::resolve_translation(size_t tr_pos, std::string templatized, const std::string &target_language, bool generate_span, std::string& span_id, std::map<std::string, std::string> &translations) +{ + size_t i = tr_pos; + + // skip the <!--#translate + i += TRANSLATE_TAG.length(); + + // and any whitespace that might follow + skip_whitespace(templatized, i); + size_t translateable_start = i; + + // now extract the word to translate + if(i < templatized.length()) + { + char c_sep = 32; + std::string what_to = extract_identifier_word(templatized, i, {'#'}, {' ', ',', '.', '!', '?', ':', ':'}, std::move(c_sep)); + skip_whitespace(templatized, i); + + std::string before_translate = templatized.substr(0, tr_pos); + std::string between = templatized.substr(translateable_start, what_to.length()); + std::string after_translate = templatized.substr(translateable_start + what_to.length() + 4); + + span_id = span_name(between); + + templatized = before_translate + (generate_span ? "<span id='" + span_id + "'>" : "") + dictionary::translate(between, target_language, true, translations) +(generate_span ? "</span>" : "") + after_translate; + } + + + return templatized; + +} + +templater_base &templater_base::templatize(const template_vector_par &v) +{ + do_not_resolve_in_get(); + std::string templatized = precalculated.empty() ? get() : precalculated; + + do_resolve_in_get(); + + templatized = resolve_loops(templatized, v); + templatized = resolve_ifeqs(templatized); + templatized = resolve_scripts(templatized, SCRIPT_TAG); + precalculated = templatized; + + return *this; +} + +void templater_base::skip_whitespace(const std::string& templatized, size_t& i) +{ + while(i < templatized.length() && std::iswspace(templatized[i])) + { + i++; + } +} + +std::string templater_base::resolve_parameters(size_t parameters_pos, std::string templatized) +{ + std::size_t include_tag_end_position = std::string::npos; + std::size_t include_tag_start_position = parameters_pos; + + parameters_pos += PARAMETERS_TAG.length(); + skip_whitespace(templatized, parameters_pos); + + size_t i = parameters_pos; + bool closing_comment_found = false; + while(i < templatized.length() && ! closing_comment_found) + { + // first: read in the variable name + char c_sep = 32; + std::string var_name = extract_identifier_word(templatized, i, {':', ' '}, {}, static_cast<char&&>(c_sep)); + + if(i == templatized.length() || var_name.empty()) + { + return templatized; + } + + std::string var_type = ""; + bool iterable = false; + + if(c_sep == ':') + { + // now read the variable type + var_type = extract_identifier_word(templatized, i); + + if(i < templatized.length() && templatized[i] == '[') + { + i ++; + skip_whitespace(templatized, i); + if(i < templatized.length() && templatized[i] == ']') + { + i ++; + } + iterable = true; + } + } + + add_parameter(var_name, var_type, iterable); + + skip_whitespace(templatized, i); + + // se if we have a "," coming up + if(templatized[i] == ',') + { + i ++; + skip_whitespace(templatized, i); + } + + if(templatized[i] == '#') // This should be the closing comment + { + closing_comment_found = check_closing_comment(templatized, i, include_tag_end_position); + } + } + + templatized.erase(include_tag_start_position, include_tag_end_position - include_tag_start_position); + return templatized; +} + +std::string templater_base::resolve_ifs(size_t if_pos, std::string templatized) +{ + size_t i = if_pos; + + // skip the <!--#if + i += IF_TAG.length(); + + // and any whitespace that might follow + skip_whitespace(templatized, i); + + // now extract the variable + if(i < templatized.length()) + { + char c_sep = 32; + std::string var_name = extract_identifier_word(templatized, i, {' ', '#'}, {}, std::move(c_sep)); + skip_whitespace(templatized, i); + + std::string var_value = ""; + // now if var_name is empty remove the if and it's body. + if(kps.count(var_name)) + { + var_value = kps[var_name]; + } + + std::string closing_endif_tag = ENDIF_TAG + " " + var_name; + std::string closing_else_tag = ELSE_TAG + " " + var_name; + + if(var_value.empty()) + { + // search for the first <!--#endif var_name + size_t endif_pos = templatized.find(closing_endif_tag); + if(endif_pos != std::string::npos) + { + // search for the first <!--#else before the endif tag + + size_t else_pos = templatized.find(closing_else_tag); + if(else_pos < endif_pos) + { + // this is the else of this if, removing the content of the if till the else + templatized = templatized.substr(0, if_pos) + templatized.substr(else_pos + closing_else_tag.length() + 4); + // we still need to remove the endif + size_t final_endif_pos = templatized.find(closing_endif_tag); + if(final_endif_pos != std::string::npos) + { + templatized = templatized.substr(0, final_endif_pos) + templatized.substr(final_endif_pos + closing_endif_tag.length() + 4); + } + + } + else + { + // no else, just remove the if tag + templatized = templatized.substr(0, if_pos) + templatized.substr(endif_pos + closing_endif_tag.length() + 4); + } + } + else + { + // oops, user forgot the enclosing endif, just return what is here + return templatized; + } + } + else // remove the comments :) + { + size_t endif_pos = templatized.find(closing_endif_tag); + + if(endif_pos != std::string::npos) + { + size_t remove_start_pos = endif_pos; + std::string remove_tag = closing_endif_tag; + size_t additional_remove = 0; // count ofchars between else and endif + // search for the first <!--#else before the endif tag + size_t else_pos = templatized.find(closing_else_tag); + if(else_pos < endif_pos) + { + remove_start_pos = else_pos; + additional_remove = endif_pos - remove_start_pos; + } + + std::string before_if = templatized.substr(0, if_pos); + std::string between = templatized.substr(i + 3, remove_start_pos - i - 3); + std::string after_endif = templatized.substr(remove_start_pos + additional_remove + closing_endif_tag.length() + 4); + + templatized = before_if + between + after_endif; + } + else + { + // oops, user forgot the enclosing endif, just return what is here + return templatized; + } + } + } + + return templatized; +} + +std::string templater_base::resolve_ifeq(size_t if_pos, std::string templatized) +{ + size_t i = if_pos; + + // skip the <!--#ifeq + i += IFEQ_TAG.length(); + + // and any whitespace that might follow + skip_whitespace(templatized, i); + + // now extract the variable + if(i < templatized.length()) + { + char c_sep = 32; + std::string what_to = extract_identifier_word(templatized, i, {' ', '#'}, {',','-'}, std::move(c_sep)); + skip_whitespace(templatized, i); + + size_t comma_pos = what_to.find(','); + bool are_eq = true; + if(comma_pos == std::string::npos) + { + are_eq = false; + } + else + { + std::string before = what_to.substr(0, comma_pos); + std::string after = what_to.substr(comma_pos + 1); + + are_eq = before == after; + } + + + if(!are_eq) + { + // search for the first <!--#endifeq + size_t endif_pos = templatized.find(ENDEQ_TAG); + if(endif_pos != std::string::npos) + { + templatized = templatized.substr(0, if_pos) + templatized.substr(endif_pos + ENDEQ_TAG.length() + 4); + } + } + else // remove the comments :) + { + size_t endif_pos = templatized.find(ENDEQ_TAG); + + if(endif_pos != std::string::npos) + { + size_t remove_start_pos = endif_pos; + std::string before_if = templatized.substr(0, if_pos); + std::string between = templatized.substr(i + 3, remove_start_pos - i - 3); + std::string after_endif = templatized.substr(remove_start_pos + ENDEQ_TAG.length() + 4); + + templatized = before_if + between + after_endif; + } + } + } + + return templatized; +} + +std::string templater_base::resolve_defines(std::string content) +{ + size_t define_pos = content.find(DEFINE_TAG); + + std::vector<std::pair<size_t, size_t>> strings_to_remove; // we will remove the define tags from in here + if(define_pos != std::string::npos) + { + while(define_pos != std::string::npos) + { + bool define_read = false; + // skipping the tag + size_t i = define_pos + DEFINE_TAG.length(); + // skipping the whitespace after the tag + skip_whitespace(content, i); + while(!define_read) + { + + std::string var_name = ""; + std::string var_value = ""; + // now comes a list of comma separated variable=value assignments or just simply variable + while(i < content.length() && content[i] != '=' && content[i] != ',' && content[i] != '#' + && (isalnum(content[i]) || content[i] == '_' )) + { + var_name += content[i]; + i ++; + } + // now if content[i] == '=' we need to parse out the value from it + if(content[i] == '=') + { + i ++; + bool string_comes = false; + if(content[i] == '"') // string comes after this? + { + i++; + string_comes = true; + } + while( (i < content.length() && content[i] != ',' && content[i] != '#' && !string_comes) + || (i < content.length() && string_comes && content[i] != '"')) + { + if(content[i] != '\\') // escaping a quote in the string + { + var_value += content[i]; + } + else + { + i++; + var_value += content[i]; + } + i++; + } + } + else + { + var_value = "true"; + } + kps.insert({var_name, var_value}); + if(content[i] == '#') + { + i++; + if(i + 2 < content.length() && content[i] == '-' && content[i+1] == '-' && content[i+2]=='>') + { + i += 3; // skip the closing comment + define_read = true; + } + else + { + // this is a malformed entry, just give up + return content; + } + } + else + { + if(content[i] == ',') + { + i++; + } + } + } + + strings_to_remove.insert(strings_to_remove.begin(), {define_pos, i}); + define_pos = content.find(DEFINE_TAG, i); + } + } + // now walk through the strings_to_remove and delete from the content everything from there + for(const auto& beg_end_pos : strings_to_remove) + { + content.erase(beg_end_pos.first, beg_end_pos.second - beg_end_pos.first); + } + + return content; +} + +std::string templater_base::resolve_scripts(std::string templatized, const std::string& tag) +{ + bool done = false; + while(!done) + { + size_t script_tag_pos = templatized.find(tag); + if(script_tag_pos != std::string::npos) + { + while(script_tag_pos != std::string::npos) + { + templatized = resolve_script(script_tag_pos, templatized, tag); + script_tag_pos = templatized.find(tag); + } + } + else + { + done = true; + } + } + return templatized; +} + +std::string templater_base::resolve_script(size_t pos, const std::string& content, const std::string &tag) +{ + size_t endscript_start_pos = content.find(ENDSCRIPT_TAG); + size_t old_pos = pos; + + // skip the tag + pos += tag.length(); + // skip the space(s) followwing the "script" + while(isspace(content[pos])) pos += 1; + + std::string script_type = ""; + while(pos < content.length() && content[pos] != '#' && content[pos] != ':') + { + script_type += content[pos++]; + } + + if(pos == content.length()) + { + set_error("Script tag at", old_pos, "is not closed"); + return content.substr(0, old_pos); + } + + static const auto script_types = {"python"}; + + bool found = false; + for(const auto& st : script_types) + { + if(st == script_type) found = true; + } + + if(!found) + { + set_error("Unsupported scripting language", script_type); + return content.substr(0, old_pos); + } + + // pos points to the script closing tag + std::string before_script = content.substr(0, old_pos); + std::string between = std::string(content.begin() + pos + 4, // #--> is the 4 + content.begin() + endscript_start_pos); + std::string after_script = content.substr(endscript_start_pos + ENDSCRIPT_TAG.length() + 4); + + std::string result = python_runner().run(kps, between, variables(true)); + + + return before_script + unafrog::utils::trim(result) + after_script; +} + +void stringholder::replace_all(const std::string &from, const std::string &to) +{ + if(from.empty()) + { + return; + } + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) + { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' + } +} + +stringholder &stringholder::templatize(const std::map<std::string, std::string> &m) +{ + for(auto it = m.begin(); it != m.end(); ++ it) + { + replace_all(TEMPLATE_VARIABLE_START_DELIMITER + it->first + TEMPLATE_VARIABLE_END_DELIMITER, it->second ); + } + return *this; +} + +template_warehouse &template_warehouse::instance() +{ + static template_warehouse twh; + return twh; +} + +bool template_warehouse::checkTemplates() +{ + for(auto it = templates.begin(); it != templates.end(); ++it) + { + if(!it->second->check()) + { + return false; + } + } + debug() << "All templates OK"; + return true; +} + +std::string template_warehouse::getTemplateContent(const std::string &templateName) +{ + if(templates.count(templateName)) + { + std::shared_ptr<template_base> tb = templates[templateName]; + std::string c = tb->content(); + return c; + } + else + { + return "Not found:" + templateName; + } +} + +bool template_warehouse::registerTemplate(const std::string &name, template_base *templateClass) +{ + // although this code seems stupid, it is due to the fact that every inclusion of the + // templater.h will cause a new object to be created, but we want only one + if(templates.count(name)) + { + debug() << "Freeing existing template class:" << name; + delete templateClass; + return false; + } + debug() << "Registering template class:" << name; + templates[name] = std::shared_ptr<template_base>(templateClass); + return true; +} + + +std::string file_template::base_dir() const +{ + namespace fs = std::filesystem; + if(fs::is_regular_file(fileName())) + { + return ""; + } + else + { + return TEMPLATES_DIRECTORY; + } + +} + +std::string file_template::content() const +{ + std::string fn = base_dir() + fileName(); + std::ifstream ifs(fn); + std::string s( (std::istreambuf_iterator<char>(ifs) ), + (std::istreambuf_iterator<char>()) ); + return s; +} + +bool file_template::check() const +{ + struct stat buffer; + bool res = (stat ( (base_dir() + fileName()).c_str(), &buffer) == 0); + if(!res) + { + debug() << "Check for "<< fileName() << " failed"; + } + return res; +} + + +
