diff --git a/Documentation/Template.md b/Documentation/Template.md index e4e34c6..a97fb2a 100644 --- a/Documentation/Template.md +++ b/Documentation/Template.md @@ -3,6 +3,7 @@ - [Variable](#variable) - [Raw Variable](#raw-variable) - [Math](#math) +- [Super Variable](#super-variable) - [Inline If](#inline-if) - [If Condition](#if-condition) - [Loop](#loop) @@ -194,6 +195,103 @@ int main() { } ``` +## Super Variable + +```txt +{svar:var, sub_var_0, sub_var_1, sub_var_2 ... , sub_var_9} +``` + +Super variable tag is like a tiny template engine engineered for basic text replacement inside a phrase; it can accept a variable tag, a raw variable tag, and a math tag. The maximum number of sub-variables is 10, starting at 0 and ending at 9. + +```txt +svar_value: when {0} met with {1}, he saw {2}, {3}, {4}, {5}, {6}, {7} and {8} but not {9}. +``` + +The order of values does not matter, and every value can be reused at any time. + +```txt +svar_value: when {5} met with {3}, he saw {2}, {1}, {4}, {0}, {6}, {7} and {8} but not {9}. +{3} and {5} were at the market when they met. +``` + +### Super Variable Example 1 + +```cpp +#include "Template.hpp" +#include "JSON.hpp" + +#include + +using Qentem::JSON; +using Qentem::StringStream; +using Qentem::Template; +using Qentem::Value; + +int main() { + Value value; + + value["phrase"] = "Welcome {0} to {1}. You have {2} points. " + "Your points will become {3} next month. " + "Your points will be here when you login to {1} every {4}."; + + value["username"] = "X"; + value["site_name"] = "Y"; + value["points"] = 10U; + value["points_html"] = R"(10U)"; + value["time"] = "week"; + + const char *content = + "{svar:phrase, {var:username}, {var:site_name}, {raw:points_html}, {math: {var:points} * 2}, {var:time}}"; + + StringStream stream; + + Template::Render(content, value, stream); + std::cout << stream << '\n'; + /* + Output: Welcome X to Y. You have 10U points. Your points will become 20 next month. Your + points will be here when you login to Y every week. + */ + + ////////////////////////////////////// + value.Reset(); + stream.Clear(); + + const char *json_data = R"( + { + "phrase": "user {0} logged in {1}.\n", + "list": [ + { + "name": "X", + "last_seen": "today" + }, + { + "name": "Y", + "last_seen": "yesterday" + }, + { + "name": "Z", + "last_seen": "last week" + } + ] + } + )"; + + value = JSON::Parse(json_data); + + content = R"({svar:phrase, {var:val[name]}, {var:val[last_seen]}})"; + + Template::Render(content, value, stream); + std::cout << '\n' << stream << '\n'; + + /* + Output: + user User1 logged in today. + user User2 logged in yesterday. + user User3 logged in last week. + */ +} +``` + ## Inline If - Logic operations: diff --git a/Examples/Template/Template18.cpp b/Examples/Template/Template18.cpp new file mode 100644 index 0000000..b04dabc --- /dev/null +++ b/Examples/Template/Template18.cpp @@ -0,0 +1,73 @@ +#include "Template.hpp" +#include "JSON.hpp" + +#include + +using Qentem::JSON; +using Qentem::StringStream; +using Qentem::Template; +using Qentem::Value; + +int main() { + Value value; + + value["phrase"] = "Welcome {0} to {1}. You have {2} points. " + "Your points will become {3} next month. " + "Your points will be here when you login to {1} every {4}."; + + value["username"] = "X"; + value["site_name"] = "Y"; + value["points"] = 10U; + value["points_html"] = R"(10U)"; + value["time"] = "week"; + + const char *content = + "{svar:phrase, {var:username}, {var:site_name}, {raw:points_html}, {math: {var:points} * 2}, {var:time}}"; + + StringStream stream; + + Template::Render(content, value, stream); + std::cout << stream << '\n'; + /* + Output: Welcome X to Y. You have 10U points. Your points will become 20 next month. Your + points will be here when you login to Y every week. + */ + + ////////////////////////////////////// + value.Reset(); + stream.Clear(); + + const char *json_data = R"( + { + "phrase": "user {0} logged in {1}.\n", + "list": [ + { + "name": "X", + "last_seen": "today" + }, + { + "name": "Y", + "last_seen": "yesterday" + }, + { + "name": "Z", + "last_seen": "last week" + } + ] + } + )"; + + value = JSON::Parse(json_data); + + content = R"({svar:phrase, {var:val[name]}, {var:val[last_seen]}})"; + + Template::Render(content, value, stream); + std::cout << '\n' << stream << '\n'; + + /* + Output: + user User1 logged in today. + user User2 logged in yesterday. + user User3 logged in last week. + */ +} diff --git a/Include/Tags.hpp b/Include/Tags.hpp index a9fd356..d61a453 100644 --- a/Include/Tags.hpp +++ b/Include/Tags.hpp @@ -30,6 +30,7 @@ namespace Qentem { namespace Tags { +struct SuperVariableTag; struct MathTag; struct LoopTag; struct InLineIfTag; @@ -37,12 +38,13 @@ struct IfTag; ////////////////////// enum struct TagType : SizeT8 { None = 0, - Variable, // {var:x} - RawVariable, // {raw:x} - Math, // {math:x} - Loop, // - InLineIf, // {if x} - If // + Variable, // {var:x} + RawVariable, // {raw:x} + SuperVariable, // {svar:x1,x2,x3} + Math, // {math:x} + Loop, // + InLineIf, // {if x} + If // }; template @@ -124,6 +126,11 @@ struct TagBit { break; } + case TagType::SuperVariable: { + data_.Storage.SetPointer(Memory::AllocateInit()); + break; + } + case TagType::Math: { data_.Storage.SetPointer(Memory::AllocateInit()); break; @@ -157,6 +164,13 @@ struct TagBit { break; } + case TagType::SuperVariable: { + SuperVariableTag *ptr = (SuperVariableTag *)(data_.GetStorage()); + Memory::Dispose(ptr); + Memory::Deallocate(ptr); + break; + } + case TagType::Math: { MathTag *ptr = (MathTag *)(data_.GetStorage()); Memory::Dispose(ptr); @@ -198,6 +212,10 @@ struct TagBit { return *(Memory::ChangePointer(data_.GetStorage())); } + inline SuperVariableTag &GetSuperVariableTag() const noexcept { + return *(Memory::ChangePointer(data_.GetStorage())); + } + inline MathTag &GetMathTag() const noexcept { return *(Memory::ChangePointer(data_.GetStorage())); } @@ -229,6 +247,14 @@ struct MathTag { SizeT EndOffset; }; +// SuperVariableTag ------------------------------------------- +struct SuperVariableTag { + Array SubTags; + VariableTag Variable; + SizeT Offset; + SizeT EndOffset; +}; + // LoopTagOptions ------------------------------------------- struct LoopTagOptions { static constexpr SizeT8 None{0}; @@ -323,7 +349,13 @@ struct TagPatterns_T { static constexpr Char_T Math_2ND_Char = MathPrefix[1U]; // Second character // static constexpr SizeT MathPrefixLength = StringUtils::ConstCount(MathPrefix); static constexpr SizeT MathPrefixLength = 6U; - static constexpr SizeT MathFullLength = (MathPrefixLength + InLineSuffixLength); + + // {svar: + static constexpr const Char_T *SuperVariablePrefix = TPStrings::SuperVariablePrefix; + static constexpr Char_T SuperVariable_2ND_Char = SuperVariablePrefix[1U]; // Second character + // static constexpr SizeT MathPrefixLength = StringUtils::ConstCount(MathPrefix); + static constexpr SizeT SuperVariablePrefixLength = 6U; + static constexpr SizeT SuperVariableFullLength = (SuperVariablePrefixLength + InLineSuffixLength); // {if static constexpr const Char_T *InLineIfPrefix = TPStrings::InLineIfPrefix; @@ -391,6 +423,8 @@ struct TagPatterns_T { static constexpr Char_T QuoteChar = '"'; + static constexpr Char_T VariablesSeparatorChar = ','; + // Inline If static constexpr Char_T CaseChar = 'a'; // c[a]se static constexpr Char_T TrueChar = 'r'; // t[r]ue @@ -406,101 +440,106 @@ struct TagPatterns_T { // char template struct TPStrings_T { - static constexpr const Char_T *VariablePrefix = "{var:"; - static constexpr const Char_T *RawVariablePrefix = "{raw:"; - static constexpr const Char_T *MathPrefix = "{math:"; - static constexpr const Char_T *InLineIfPrefix = "{if"; - static constexpr const Char_T *LoopPrefix = " struct TPStrings_T { - static constexpr const Char_T *VariablePrefix = u"{var:"; - static constexpr const Char_T *RawVariablePrefix = u"{raw:"; - static constexpr const Char_T *MathPrefix = u"{math:"; - static constexpr const Char_T *InLineIfPrefix = u"{if"; - static constexpr const Char_T *LoopPrefix = u""; - static constexpr const Char_T *IfPrefix = u""; - static constexpr const Char_T *ElsePrefix = u""; - static constexpr const Char_T *HTMLAnd = u"&"; - static constexpr const Char_T *HTMLLess = u"<"; - static constexpr const Char_T *HTMLGreater = u">"; - static constexpr const Char_T *HTMLQuote = u"""; - static constexpr const Char_T *HTMLSingleQuote = u"'"; + static constexpr const Char_T *VariablePrefix = u"{var:"; + static constexpr const Char_T *RawVariablePrefix = u"{raw:"; + static constexpr const Char_T *SuperVariablePrefix = u"{svar:"; + static constexpr const Char_T *MathPrefix = u"{math:"; + static constexpr const Char_T *InLineIfPrefix = u"{if"; + static constexpr const Char_T *LoopPrefix = u""; + static constexpr const Char_T *IfPrefix = u""; + static constexpr const Char_T *ElsePrefix = u""; + static constexpr const Char_T *HTMLAnd = u"&"; + static constexpr const Char_T *HTMLLess = u"<"; + static constexpr const Char_T *HTMLGreater = u">"; + static constexpr const Char_T *HTMLQuote = u"""; + static constexpr const Char_T *HTMLSingleQuote = u"'"; }; // char32_t template struct TPStrings_T { - static constexpr const Char_T *VariablePrefix = U"{var:"; - static constexpr const Char_T *RawVariablePrefix = U"{raw:"; - static constexpr const Char_T *MathPrefix = U"{math:"; - static constexpr const Char_T *InLineIfPrefix = U"{if"; - static constexpr const Char_T *LoopPrefix = U""; - static constexpr const Char_T *IfPrefix = U""; - static constexpr const Char_T *ElsePrefix = U""; - static constexpr const Char_T *HTMLAnd = U"&"; - static constexpr const Char_T *HTMLLess = U"<"; - static constexpr const Char_T *HTMLGreater = U">"; - static constexpr const Char_T *HTMLQuote = U"""; - static constexpr const Char_T *HTMLSingleQuote = U"'"; + static constexpr const Char_T *VariablePrefix = U"{var:"; + static constexpr const Char_T *RawVariablePrefix = U"{raw:"; + static constexpr const Char_T *SuperVariablePrefix = U"{svar:"; + static constexpr const Char_T *MathPrefix = U"{math:"; + static constexpr const Char_T *InLineIfPrefix = U"{if"; + static constexpr const Char_T *LoopPrefix = U""; + static constexpr const Char_T *IfPrefix = U""; + static constexpr const Char_T *ElsePrefix = U""; + static constexpr const Char_T *HTMLAnd = U"&"; + static constexpr const Char_T *HTMLLess = U"<"; + static constexpr const Char_T *HTMLGreater = U">"; + static constexpr const Char_T *HTMLQuote = U"""; + static constexpr const Char_T *HTMLSingleQuote = U"'"; }; // wchar_t size = 4 template <> struct TPStrings_T { - static constexpr const wchar_t *VariablePrefix = L"{var:"; - static constexpr const wchar_t *RawVariablePrefix = L"{raw:"; - static constexpr const wchar_t *MathPrefix = L"{math:"; - static constexpr const wchar_t *InLineIfPrefix = L"{if"; - static constexpr const wchar_t *LoopPrefix = L""; - static constexpr const wchar_t *IfPrefix = L""; - static constexpr const wchar_t *ElsePrefix = L""; - static constexpr const wchar_t *HTMLAnd = L"&"; - static constexpr const wchar_t *HTMLLess = L"<"; - static constexpr const wchar_t *HTMLGreater = L">"; - static constexpr const wchar_t *HTMLQuote = L"""; - static constexpr const wchar_t *HTMLSingleQuote = L"'"; + static constexpr const wchar_t *VariablePrefix = L"{var:"; + static constexpr const wchar_t *RawVariablePrefix = L"{raw:"; + static constexpr const wchar_t *SuperVariablePrefix = L"{svar:"; + static constexpr const wchar_t *MathPrefix = L"{math:"; + static constexpr const wchar_t *InLineIfPrefix = L"{if"; + static constexpr const wchar_t *LoopPrefix = L""; + static constexpr const wchar_t *IfPrefix = L""; + static constexpr const wchar_t *ElsePrefix = L""; + static constexpr const wchar_t *HTMLAnd = L"&"; + static constexpr const wchar_t *HTMLLess = L"<"; + static constexpr const wchar_t *HTMLGreater = L">"; + static constexpr const wchar_t *HTMLQuote = L"""; + static constexpr const wchar_t *HTMLSingleQuote = L"'"; }; // wchar_t size = 2 template <> struct TPStrings_T { - static constexpr const wchar_t *VariablePrefix = L"{var:"; - static constexpr const wchar_t *RawVariablePrefix = L"{raw:"; - static constexpr const wchar_t *MathPrefix = L"{math:"; - static constexpr const wchar_t *InLineIfPrefix = L"{if"; - static constexpr const wchar_t *LoopPrefix = L""; - static constexpr const wchar_t *IfPrefix = L""; - static constexpr const wchar_t *ElsePrefix = L""; - static constexpr const wchar_t *HTMLAnd = L"&"; - static constexpr const wchar_t *HTMLLess = L"<"; - static constexpr const wchar_t *HTMLGreater = L">"; - static constexpr const wchar_t *HTMLQuote = L"""; - static constexpr const wchar_t *HTMLSingleQuote = L"'"; + static constexpr const wchar_t *VariablePrefix = L"{var:"; + static constexpr const wchar_t *RawVariablePrefix = L"{raw:"; + static constexpr const wchar_t *SuperVariablePrefix = L"{svar:"; + static constexpr const wchar_t *MathPrefix = L"{math:"; + static constexpr const wchar_t *InLineIfPrefix = L"{if"; + static constexpr const wchar_t *LoopPrefix = L""; + static constexpr const wchar_t *IfPrefix = L""; + static constexpr const wchar_t *ElsePrefix = L""; + static constexpr const wchar_t *HTMLAnd = L"&"; + static constexpr const wchar_t *HTMLLess = L"<"; + static constexpr const wchar_t *HTMLGreater = L">"; + static constexpr const wchar_t *HTMLQuote = L"""; + static constexpr const wchar_t *HTMLSingleQuote = L"'"; }; } // namespace Tags diff --git a/Include/Template.hpp b/Include/Template.hpp index cf9440d..8c0ca17 100644 --- a/Include/Template.hpp +++ b/Include/Template.hpp @@ -47,9 +47,17 @@ namespace Qentem { * - var|e|n: Raw variable, Equation or Number. * * - * - {if case="var|s" true="rvar|var|s" false="rvar|var|s"} + * - {svar:var, var|e|n, var|e|n, ... } * - * - Inline if, rvar: Raw variable, var: Variable, s: String. + * - var: variable only. + * - sub variables, var|e|n: Raw variable, Equation or Number. + * - max sub variables 10; (0-9). + * + * + * - {if case="var|e|n" true="rvar|var|s" false="rvar|var|s"} + * + * - case, var|e|n: Raw variable, Equation or Number. + * - true and false, rvar: Raw variable, var: Variable, s: String. * * * <...> @@ -111,6 +119,22 @@ namespace Qentem { * {math: 0.2 + 0.3} */ +/* + * Super Variable Tag: + * + * {svar:welcome_x_to_y, {var:name}, {var:site}} + * {svar:welcome_x_to_y_its_z_at_your_city, {var:name}, {var:site}, {math: 10+3}, {raw:city}} + * + * welcome_x_to_y: "Welcome {0} to {1}." + * welcome_x_to_y_its_z_at_your_city: "Welcome {0} to {1}. its {2}:am in your {3}." + * + * {svar:hello_x_your_name_was_y_now_x_y_is_better_x, {var:name2}, {var:name1}} + * + * hello_x_your_name_was_y_now_x_y_is_better_x: "Hello {1}. Your name was {0}, now it's {1}, {1} is better than {0}." + * + * Note: sub variables can be used in any order any time. + */ + /* * Inline If Tag: * @@ -246,19 +270,20 @@ struct TemplateSub { } private: - using TagType = Tags::TagType; - using VariableTag = Tags::VariableTag; - using MathTag = Tags::MathTag; - using LoopTag = Tags::LoopTag; - using LoopTagOptions = Tags::LoopTagOptions; - using InLineIfTag = Tags::InLineIfTag; - using IfTagCase = Tags::IfTagCase; - using IfTag = Tags::IfTag; - using TagBit = Tags::TagBit; - using QExpressions = Array; - using QOperation = QExpression::QOperation; - using ExpressionType = QExpression::ExpressionType; - using TagPatterns = Tags::TagPatterns_T; + using TagType = Tags::TagType; + using VariableTag = Tags::VariableTag; + using SuperVariableTag = Tags::SuperVariableTag; + using MathTag = Tags::MathTag; + using LoopTag = Tags::LoopTag; + using LoopTagOptions = Tags::LoopTagOptions; + using InLineIfTag = Tags::InLineIfTag; + using IfTagCase = Tags::IfTagCase; + using IfTag = Tags::IfTag; + using TagBit = Tags::TagBit; + using QExpressions = Array; + using QOperation = QExpression::QOperation; + using ExpressionType = QExpression::ExpressionType; + using TagPatterns = Tags::TagPatterns_T; public: void Render(const TagBit *tag, const TagBit *end) const { @@ -302,6 +327,11 @@ struct TemplateSub { break; } + case TagType::SuperVariable: { + renderSuperVariable(tag->GetSuperVariableTag(), offset); + break; + } + case TagType::Loop: { renderLoop(tag->GetLoopTag(), offset); break; @@ -409,6 +439,30 @@ struct TemplateSub { break; } + case TagPatterns::SuperVariable_2ND_Char: { + static constexpr const Char_T *s_var_prefix_p_2 = (TagPatterns::SuperVariablePrefix + SizeT{2}); + constexpr SizeT s_var_prefix_length_m_2 = (TagPatterns::SuperVariablePrefixLength - SizeT{2}); + ++current_offset; + + if (StringUtils::IsEqual(s_var_prefix_p_2, (content_ + current_offset), + s_var_prefix_length_m_2)) { + current_offset += s_var_prefix_length_m_2; + current_offset = + Engine::SkipInnerPatterns(TagPatterns::InLinePrefix, TagPatterns::InLineSuffix, + content_, current_offset, end_offset, length_); + + if (current_offset != SizeT{0}) { + const TagBit &tag = tags_cache.Insert(TagBit{TagType::SuperVariable}); + + parseSuperVariableTag(offset, current_offset, tag.GetSuperVariableTag()); + offset = current_offset; + continue; + } + } + + break; + } + case TagPatterns::InlineIf_2ND_Char: { static constexpr const Char_T *i_if_prefix_p_2 = (TagPatterns::InLineIfPrefix + SizeT{2}); constexpr SizeT i_if_prefix_length_m_2 = (TagPatterns::InLineIfPrefixLength - SizeT{2}); @@ -701,6 +755,78 @@ struct TemplateSub { } } + void renderSuperVariable(const SuperVariableTag &tag, SizeT &offset) const { + const Value_T *s_var = getValue(tag.Variable); + const Char_T *content = nullptr; + SizeT length = 0; + + stream_->Write((content_ + offset), (tag.Offset - offset)); + offset = tag.EndOffset; + + if ((s_var != nullptr) && s_var->SetCharAndLength(content, length)) { + SizeT index = 0; + SizeT last_index = 0; + + while (index < length) { + if (content[index] == TagPatterns::InLinePrefix) { + const SizeT start = index; + + escapeHTMLSpecialChars(*stream_, (content + last_index), (start - last_index)); + last_index = start; + ++index; + + if (index < length) { + const SizeT id = SizeT(content[index] - DigitUtils::DigitChar::Zero); + ++index; + + if ((index < length) && (content[index] == TagPatterns::InLineSuffix)) { + ++index; + + if (id < tag.SubTags.Size()) { + const TagBit *sub_tag = (tag.SubTags.First() + id); + last_index = index; + + switch (sub_tag->GetType()) { + case TagType::Variable: { + const VariableTag &var = sub_tag->GeVariableTag(); + SizeT var_offset = (var.Offset - TagPatterns::VariablePrefixLength); + renderVariable(var, var_offset); + break; + } + + case TagType::RawVariable: { + const VariableTag &r_var = sub_tag->GeRawVariableTag(); + SizeT r_var_offset = (r_var.Offset - TagPatterns::RawVariablePrefixLength); + renderRawVariable(r_var, r_var_offset); + break; + } + + case TagType::Math: { + const MathTag &math = sub_tag->GetMathTag(); + SizeT math_offset = math.Offset; + renderMath(math, math_offset); + break; + } + + default: + break; + } + + continue; + } + } + } + } + + ++index; + } + + escapeHTMLSpecialChars(*stream_, (content + last_index), (index - last_index)); + } else { + stream_->Write((content_ + tag.Offset), (tag.EndOffset - tag.Offset)); + } + } + void renderLoop(const LoopTag &tag, SizeT &offset) const { Value_T grouped_set; const Value_T *loop_set; @@ -874,6 +1000,27 @@ struct TemplateSub { tag.Expressions = parseExpressions(offset, end_offset); } + void parseSuperVariableTag(SizeT offset, SizeT end_offset, SuperVariableTag &tag) const { + tag.Offset = offset; + tag.EndOffset = end_offset; + + offset += TagPatterns::MathPrefixLength; + end_offset -= TagPatterns::InLineSuffixLength; + + SizeT var_end_offset = offset; + + while ((var_end_offset < end_offset) && (content_[var_end_offset] != TagPatterns::VariablesSeparatorChar)) { + ++var_end_offset; + } + + if (var_end_offset < end_offset) { + parseVariableTag(offset, var_end_offset, tag.Variable); + ++var_end_offset; + + lightParse(tag.SubTags, var_end_offset, end_offset); + } + } + /* * Gets everything between "..." */ diff --git a/Tests/TemplateLTest.hpp b/Tests/TemplateLTest.hpp index 1af77ad..863408d 100644 --- a/Tests/TemplateLTest.hpp +++ b/Tests/TemplateLTest.hpp @@ -2215,6 +2215,169 @@ static void TestMathLTag2(QTest &test) { ss.Clear(); } +static void TestSuperVariableLTag1(QTest &test) { + StringStream ss; + Value value; + + value[LR"(a)"] = LR"(a)"; + value[LR"(b)"] = LR"(bb)"; + value[LR"(c)"] = LR"(ccc)"; + value[LR"(d)"] = LR"(dddd)"; + value[LR"(4)"] = LR"(e)"; + value[LR"(e)"] = LR"(f)"; + value[LR"(ffffff)"] = LR"(g)"; + value[LR"(g)"] = 10; + value[LR"(h)"] = true; + value[LR"(999999999)"] = LR"(8000000000007779999999)"; + + value[LR"(x_y_z)"] = LR"(the value is {0})"; + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:a}})", value, ss), LR"(the value is a)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(LR"(..{svar:x_y_z, {var:d}}--)", value, ss), LR"(..the value is dddd--)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(LR"(.{svar:x_y_z, {var:g}}-)", value, ss), LR"(.the value is 10-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(LR"(.{svar:x_y_z, {var:h}})", value, ss), LR"(.the value is true)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:999999999}}x)", value, ss), + LR"(the value is 8000000000007779999999x)", __LINE__); + ss.Clear(); + + value[LR"(x_y_z)"] = LR"({0})"; + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:4}})", value, ss), LR"(e)", __LINE__); + ss.Clear(); + + value[LR"(x_y_z)"] = LR"({0}{0} {0} {0}{0}{0})"; + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:ffffff}})", value, ss), LR"(gg g ggg)", __LINE__); + ss.Clear(); + + value[LR"(x_y_z)"] = LR"({0},{1}.{2}-{3}**{4}////{5}=={6} {7} {8} {9})"; + + test.IsEqual( + Template::Render( + LR"({svar:x_y_z, {var:ffffff}, {var:a}, {var:4}, {var:999999999}, {var:g}, {var:h}, {var:c}, {var:d}, {var:b}, {var:e}})", + value, ss), + LR"(g,a.e-8000000000007779999999**10////true==ccc dddd bb f)", __LINE__); + ss.Clear(); + + value[LR"(a)"] = LR"('a)"; + value[LR"(b)"] = LR"(<>bb)"; + + value[LR"(x_y_z)"] = LR"({0},{1}.{2}-{3}**{4}////{5}=={6} {7} {8} {9})"; + + if (Config::AutoEscapeHTML) { + test.IsEqual( + Template::Render( + LR"({svar:x_y_z, {var:ffffff}, {var:a}, {var:4}, {var:999999999}, {var:g}, {var:h}, {var:c}, {var:d}, {var:b}, {var:e}})", + value, ss), + LR"(g,'a.e-8000000000007779999999**10////true==ccc dddd <>bb f)", __LINE__); + } else { + test.IsEqual( + Template::Render( + LR"({svar:x_y_z, {var:ffffff}, {var:a}, {var:4}, {var:999999999}, {var:g}, {var:h}, {var:c}, {var:d}, {var:b}, {var:e}})", + value, ss), + LR"(g,'a.e-8000000000007779999999**10////true==ccc dddd <>bb f)", __LINE__); + } + + ss.Clear(); + + test.IsEqual( + Template::Render( + LR"({svar:x_y_z, {raw:ffffff}, {raw:a}, {raw:4}, {raw:999999999}, {raw:g}, {raw:h}, {raw:c}, {raw:d}, {raw:b}, {raw:e}})", + value, ss), + LR"(g,'a.e-8000000000007779999999**10////true==ccc dddd <>bb f)", __LINE__); + ss.Clear(); + + value[LR"(a)"] = 10; + value[LR"(b)"] = 5; + value[LR"(c)"] = 3; + + value[LR"(x_y_z)"] = LR"({0} {1} {2} {3} {4})"; + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {math:{var:a}+{var:b}+{var:c}}, + {math:{var:a}*{var:b}*{var:c}}, + {math:{var:a}-{var:b}-{var:c}}, + {math:{var:a}+{var:b}-{var:c}}, + {math:{var:a}/{var:b}+{var:c}}})", + value, ss), + LR"(18 150 2 12 5)", __LINE__); +} + +static void TestSuperVariableLTag2(QTest &test) { + StringStream ss; + Value value; + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:a}})", value, ss), LR"({svar:x_y_z, {var:a}})", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), + LR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", __LINE__); + ss.Clear(); + + value[LR"(x_y_z)"] = LR"({0})"; + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}})", value, ss), LR"({var:x})", __LINE__); + ss.Clear(); + + value[LR"(x_y_z)"] = LR"({0} {1})"; + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}, {var:y}})", value, ss), LR"({var:x} {var:y})", __LINE__); + ss.Clear(); + + value[LR"(x_y_z)"] = LR"(-{0}-{1}-{2}-)"; + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), + LR"(-{var:x}-{var:y}-{var:z}-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}})", value, ss), LR"(-{var:x}-{1}-{2}-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}, {var:x}})", value, ss), LR"(-{var:x}-{var:x}-{2}-)", + __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}, {var:x}})", value, ss), LR"(-{var:x}-{var:x}-{2}-)", + __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}, {var:x}, {var:x}})", value, ss), + LR"(-{var:x}-{var:x}-{var:x}-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), + LR"(-{var:x}-{var:y}-{var:z}-)", __LINE__); + ss.Clear(); + + value[LR"(x_y_z)"] = LR"(-{0-{1-{2-)"; + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), LR"(-{0-{1-{2-)", + __LINE__); + ss.Clear(); + + value[LR"(x_y_z)"] = LR"(-{{{-)"; + + test.IsEqual(Template::Render(LR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), LR"(-{{{-)", __LINE__); + ss.Clear(); + + value[LR"(x_y_z)"] = LR"({0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11})"; + value[LR"(z)"] = 1; + + test.IsEqual( + Template::Render( + LR"({svar:x_y_z, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}})", + value, ss), + LR"(1111111111{10}{11})", __LINE__); + ss.Clear(); +} + static void TestInlineIfLTag(QTest &test) { StringStream ss; Value value; @@ -3470,6 +3633,9 @@ static int RunTemplateLTests() { test.Test("Math Tag Test 1", TestMathLTag1); test.Test("Math Tag Test 2", TestMathLTag2); + test.Test("Super Variable Tag Test 1", TestSuperVariableLTag1); + test.Test("Super Variable Tag Test 2", TestSuperVariableLTag2); + test.Test("Inline if Tag Test", TestInlineIfLTag); test.Test("Loop Tag Test 1", TestLoopLTag1); diff --git a/Tests/TemplateTest.hpp b/Tests/TemplateTest.hpp index 93c8a93..c411a2b 100644 --- a/Tests/TemplateTest.hpp +++ b/Tests/TemplateTest.hpp @@ -2214,6 +2214,168 @@ static void TestMathTag2(QTest &test) { ss.Clear(); } +static void TestSuperVariableTag1(QTest &test) { + StringStream ss; + Value value; + + value[R"(a)"] = R"(a)"; + value[R"(b)"] = R"(bb)"; + value[R"(c)"] = R"(ccc)"; + value[R"(d)"] = R"(dddd)"; + value[R"(4)"] = R"(e)"; + value[R"(e)"] = R"(f)"; + value[R"(ffffff)"] = R"(g)"; + value[R"(g)"] = 10; + value[R"(h)"] = true; + value[R"(999999999)"] = R"(8000000000007779999999)"; + + value[R"(x_y_z)"] = R"(the value is {0})"; + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:a}})", value, ss), R"(the value is a)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(R"(..{svar:x_y_z, {var:d}}--)", value, ss), R"(..the value is dddd--)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(R"(.{svar:x_y_z, {var:g}}-)", value, ss), R"(.the value is 10-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(R"(.{svar:x_y_z, {var:h}})", value, ss), R"(.the value is true)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:999999999}}x)", value, ss), + R"(the value is 8000000000007779999999x)", __LINE__); + ss.Clear(); + + value[R"(x_y_z)"] = R"({0})"; + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:4}})", value, ss), R"(e)", __LINE__); + ss.Clear(); + + value[R"(x_y_z)"] = R"({0}{0} {0} {0}{0}{0})"; + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:ffffff}})", value, ss), R"(gg g ggg)", __LINE__); + ss.Clear(); + + value[R"(x_y_z)"] = R"({0},{1}.{2}-{3}**{4}////{5}=={6} {7} {8} {9})"; + + test.IsEqual( + Template::Render( + R"({svar:x_y_z, {var:ffffff}, {var:a}, {var:4}, {var:999999999}, {var:g}, {var:h}, {var:c}, {var:d}, {var:b}, {var:e}})", + value, ss), + R"(g,a.e-8000000000007779999999**10////true==ccc dddd bb f)", __LINE__); + ss.Clear(); + + value[R"(a)"] = R"('a)"; + value[R"(b)"] = R"(<>bb)"; + + value[R"(x_y_z)"] = R"({0},{1}.{2}-{3}**{4}////{5}=={6} {7} {8} {9})"; + + if (Config::AutoEscapeHTML) { + test.IsEqual( + Template::Render( + R"({svar:x_y_z, {var:ffffff}, {var:a}, {var:4}, {var:999999999}, {var:g}, {var:h}, {var:c}, {var:d}, {var:b}, {var:e}})", + value, ss), + R"(g,'a.e-8000000000007779999999**10////true==ccc dddd <>bb f)", __LINE__); + } else { + test.IsEqual( + Template::Render( + R"({svar:x_y_z, {var:ffffff}, {var:a}, {var:4}, {var:999999999}, {var:g}, {var:h}, {var:c}, {var:d}, {var:b}, {var:e}})", + value, ss), + R"(g,'a.e-8000000000007779999999**10////true==ccc dddd <>bb f)", __LINE__); + } + + ss.Clear(); + + test.IsEqual( + Template::Render( + R"({svar:x_y_z, {raw:ffffff}, {raw:a}, {raw:4}, {raw:999999999}, {raw:g}, {raw:h}, {raw:c}, {raw:d}, {raw:b}, {raw:e}})", + value, ss), + R"(g,'a.e-8000000000007779999999**10////true==ccc dddd <>bb f)", __LINE__); + ss.Clear(); + + value[R"(a)"] = 10; + value[R"(b)"] = 5; + value[R"(c)"] = 3; + + value[R"(x_y_z)"] = R"({0} {1} {2} {3} {4})"; + + test.IsEqual(Template::Render(R"({svar:x_y_z, {math:{var:a}+{var:b}+{var:c}}, + {math:{var:a}*{var:b}*{var:c}}, + {math:{var:a}-{var:b}-{var:c}}, + {math:{var:a}+{var:b}-{var:c}}, + {math:{var:a}/{var:b}+{var:c}}})", + value, ss), + R"(18 150 2 12 5)", __LINE__); +} + +static void TestSuperVariableTag2(QTest &test) { + StringStream ss; + Value value; + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:a}})", value, ss), R"({svar:x_y_z, {var:a}})", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), + R"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", __LINE__); + ss.Clear(); + + value[R"(x_y_z)"] = R"({0})"; + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}})", value, ss), R"({var:x})", __LINE__); + ss.Clear(); + + value[R"(x_y_z)"] = R"({0} {1})"; + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}, {var:y}})", value, ss), R"({var:x} {var:y})", __LINE__); + ss.Clear(); + + value[R"(x_y_z)"] = R"(-{0}-{1}-{2}-)"; + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), + R"(-{var:x}-{var:y}-{var:z}-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}})", value, ss), R"(-{var:x}-{1}-{2}-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}, {var:x}})", value, ss), R"(-{var:x}-{var:x}-{2}-)", + __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}, {var:x}})", value, ss), R"(-{var:x}-{var:x}-{2}-)", + __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}, {var:x}, {var:x}})", value, ss), + R"(-{var:x}-{var:x}-{var:x}-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), + R"(-{var:x}-{var:y}-{var:z}-)", __LINE__); + ss.Clear(); + + value[R"(x_y_z)"] = R"(-{0-{1-{2-)"; + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), R"(-{0-{1-{2-)", __LINE__); + ss.Clear(); + + value[R"(x_y_z)"] = R"(-{{{-)"; + + test.IsEqual(Template::Render(R"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), R"(-{{{-)", __LINE__); + ss.Clear(); + + value[R"(x_y_z)"] = R"({0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11})"; + value[R"(z)"] = 1; + + test.IsEqual( + Template::Render( + R"({svar:x_y_z, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}})", + value, ss), + R"(1111111111{10}{11})", __LINE__); + ss.Clear(); +} + static void TestInlineIfTag(QTest &test) { StringStream ss; Value value; @@ -3468,6 +3630,9 @@ static int RunTemplateTests() { test.Test("Math Tag Test 1", TestMathTag1); test.Test("Math Tag Test 2", TestMathTag2); + test.Test("Super Variable Tag Test 1", TestSuperVariableTag1); + test.Test("Super Variable Tag Test 2", TestSuperVariableTag2); + test.Test("Inline if Tag Test", TestInlineIfTag); test.Test("Loop Tag Test 1", TestLoopTag1); diff --git a/Tests/TemplateUTest.hpp b/Tests/TemplateUTest.hpp index 10a60bd..871bf3d 100644 --- a/Tests/TemplateUTest.hpp +++ b/Tests/TemplateUTest.hpp @@ -2216,6 +2216,169 @@ static void TestMathUTag2(QTest &test) { ss.Clear(); } +static void TestSuperVariableUTag1(QTest &test) { + StringStream ss; + Value value; + + value[uR"(a)"] = uR"(a)"; + value[uR"(b)"] = uR"(bb)"; + value[uR"(c)"] = uR"(ccc)"; + value[uR"(d)"] = uR"(dddd)"; + value[uR"(4)"] = uR"(e)"; + value[uR"(e)"] = uR"(f)"; + value[uR"(ffffff)"] = uR"(g)"; + value[uR"(g)"] = 10; + value[uR"(h)"] = true; + value[uR"(999999999)"] = uR"(8000000000007779999999)"; + + value[uR"(x_y_z)"] = uR"(the value is {0})"; + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:a}})", value, ss), uR"(the value is a)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(uR"(..{svar:x_y_z, {var:d}}--)", value, ss), uR"(..the value is dddd--)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(uR"(.{svar:x_y_z, {var:g}}-)", value, ss), uR"(.the value is 10-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(uR"(.{svar:x_y_z, {var:h}})", value, ss), uR"(.the value is true)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:999999999}}x)", value, ss), + uR"(the value is 8000000000007779999999x)", __LINE__); + ss.Clear(); + + value[uR"(x_y_z)"] = uR"({0})"; + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:4}})", value, ss), uR"(e)", __LINE__); + ss.Clear(); + + value[uR"(x_y_z)"] = uR"({0}{0} {0} {0}{0}{0})"; + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:ffffff}})", value, ss), uR"(gg g ggg)", __LINE__); + ss.Clear(); + + value[uR"(x_y_z)"] = uR"({0},{1}.{2}-{3}**{4}////{5}=={6} {7} {8} {9})"; + + test.IsEqual( + Template::Render( + uR"({svar:x_y_z, {var:ffffff}, {var:a}, {var:4}, {var:999999999}, {var:g}, {var:h}, {var:c}, {var:d}, {var:b}, {var:e}})", + value, ss), + uR"(g,a.e-8000000000007779999999**10////true==ccc dddd bb f)", __LINE__); + ss.Clear(); + + value[uR"(a)"] = uR"('a)"; + value[uR"(b)"] = uR"(<>bb)"; + + value[uR"(x_y_z)"] = uR"({0},{1}.{2}-{3}**{4}////{5}=={6} {7} {8} {9})"; + + if (Config::AutoEscapeHTML) { + test.IsEqual( + Template::Render( + uR"({svar:x_y_z, {var:ffffff}, {var:a}, {var:4}, {var:999999999}, {var:g}, {var:h}, {var:c}, {var:d}, {var:b}, {var:e}})", + value, ss), + uR"(g,'a.e-8000000000007779999999**10////true==ccc dddd <>bb f)", __LINE__); + } else { + test.IsEqual( + Template::Render( + uR"({svar:x_y_z, {var:ffffff}, {var:a}, {var:4}, {var:999999999}, {var:g}, {var:h}, {var:c}, {var:d}, {var:b}, {var:e}})", + value, ss), + uR"(g,'a.e-8000000000007779999999**10////true==ccc dddd <>bb f)", __LINE__); + } + + ss.Clear(); + + test.IsEqual( + Template::Render( + uR"({svar:x_y_z, {raw:ffffff}, {raw:a}, {raw:4}, {raw:999999999}, {raw:g}, {raw:h}, {raw:c}, {raw:d}, {raw:b}, {raw:e}})", + value, ss), + uR"(g,'a.e-8000000000007779999999**10////true==ccc dddd <>bb f)", __LINE__); + ss.Clear(); + + value[uR"(a)"] = 10; + value[uR"(b)"] = 5; + value[uR"(c)"] = 3; + + value[uR"(x_y_z)"] = uR"({0} {1} {2} {3} {4})"; + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {math:{var:a}+{var:b}+{var:c}}, + {math:{var:a}*{var:b}*{var:c}}, + {math:{var:a}-{var:b}-{var:c}}, + {math:{var:a}+{var:b}-{var:c}}, + {math:{var:a}/{var:b}+{var:c}}})", + value, ss), + uR"(18 150 2 12 5)", __LINE__); +} + +static void TestSuperVariableUTag2(QTest &test) { + StringStream ss; + Value value; + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:a}})", value, ss), uR"({svar:x_y_z, {var:a}})", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), + uR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", __LINE__); + ss.Clear(); + + value[uR"(x_y_z)"] = uR"({0})"; + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}})", value, ss), uR"({var:x})", __LINE__); + ss.Clear(); + + value[uR"(x_y_z)"] = uR"({0} {1})"; + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}, {var:y}})", value, ss), uR"({var:x} {var:y})", __LINE__); + ss.Clear(); + + value[uR"(x_y_z)"] = uR"(-{0}-{1}-{2}-)"; + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), + uR"(-{var:x}-{var:y}-{var:z}-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}})", value, ss), uR"(-{var:x}-{1}-{2}-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}, {var:x}})", value, ss), uR"(-{var:x}-{var:x}-{2}-)", + __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}, {var:x}})", value, ss), uR"(-{var:x}-{var:x}-{2}-)", + __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}, {var:x}, {var:x}})", value, ss), + uR"(-{var:x}-{var:x}-{var:x}-)", __LINE__); + ss.Clear(); + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), + uR"(-{var:x}-{var:y}-{var:z}-)", __LINE__); + ss.Clear(); + + value[uR"(x_y_z)"] = uR"(-{0-{1-{2-)"; + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), uR"(-{0-{1-{2-)", + __LINE__); + ss.Clear(); + + value[uR"(x_y_z)"] = uR"(-{{{-)"; + + test.IsEqual(Template::Render(uR"({svar:x_y_z, {var:x}, {var:y}, {var:z}})", value, ss), uR"(-{{{-)", __LINE__); + ss.Clear(); + + value[uR"(x_y_z)"] = uR"({0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11})"; + value[uR"(z)"] = 1; + + test.IsEqual( + Template::Render( + uR"({svar:x_y_z, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}, {var:z}})", + value, ss), + uR"(1111111111{10}{11})", __LINE__); + ss.Clear(); +} + static void TestInlineIfUTag(QTest &test) { StringStream ss; Value value; @@ -3471,6 +3634,9 @@ static int RunTemplateUTests() { test.Test("Math Tag Test 1", TestMathUTag1); test.Test("Math Tag Test 2", TestMathUTag2); + test.Test("Super Variable Tag Test 1", TestSuperVariableUTag1); + test.Test("Super Variable Tag Test 2", TestSuperVariableUTag2); + test.Test("Inline if Tag Test", TestInlineIfUTag); test.Test("Loop Tag Test 1", TestLoopUTag1);