28 using std::regex_search;
40 using MapS = std::map<string, string>;
41 using LERR_(ITER_EXHAUST);
43 using text_template::ACCEPT_MARKUP;
44 using text_template::TagSyntax;
78 MapS snaps{{
"whatever",
"cruel world"}
79 ,{
"greeting",
"farewell"}};
81 ==
"farewell cruel world ↯"_expect);
99 CHECK (not regex_search (input, mat, ACCEPT_MARKUP));
101 input =
" Hallelujah ";
102 CHECK (not regex_search (input, mat, ACCEPT_MARKUP));
104 input =
" stale${beer}forever";
105 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
106 CHECK (mat.position() == 6);
107 CHECK (mat.length() == 7);
108 CHECK (mat.prefix() ==
" stale"_expect);
109 CHECK (mat.suffix() ==
"forever"_expect);
110 CHECK (mat[0] ==
"${beer}"_expect);
111 CHECK (not mat[1].matched);
112 CHECK (not mat[2].matched);
113 CHECK (not mat[3].matched);
114 CHECK (not mat[4].matched);
115 CHECK (mat[5] ==
"beer"_expect);
117 input =
" watch ${for stale}${beer} whatever ";
118 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
119 CHECK (mat.position() == 7);
120 CHECK (mat.length() == 12);
121 CHECK (mat.prefix() ==
" watch "_expect);
122 CHECK (mat.suffix() ==
"${beer} whatever "_expect);
123 CHECK (mat[0] ==
"${for stale}"_expect);
124 CHECK (not mat[2].matched);
125 CHECK (not mat[3].matched);
126 CHECK (mat[4] ==
"for"_expect);
127 CHECK (mat[5] ==
"stale"_expect);
129 input =
" work ${ end if beer \t } however ";
130 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
131 CHECK (mat.position() == 6);
132 CHECK (mat.length() == 19);
133 CHECK (mat.prefix() ==
" work "_expect);
134 CHECK (mat.suffix() ==
" however "_expect);
135 CHECK (mat[0] ==
"${ end if beer \t }"_expect);
136 CHECK (mat[3] ==
"end "_expect);
137 CHECK (mat[4] ==
"if"_expect);
138 CHECK (mat[5] ==
"beer"_expect);
140 input =
" catch ${endgame stale}${endfor brown.beer} ever ";
141 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
142 CHECK (mat.position() == 23);
143 CHECK (mat.length() == 20);
144 CHECK (mat.prefix() ==
" catch ${endgame stale}"_expect);
145 CHECK (mat.suffix() ==
" ever "_expect);
146 CHECK (mat[0] ==
"${endfor brown.beer}"_expect);
147 CHECK (mat[3] ==
"end"_expect);
148 CHECK (mat[4] ==
"for"_expect);
149 CHECK (mat[5] ==
"brown.beer"_expect);
151 input =
" catch ${else} ever ";
152 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
153 CHECK (mat.position() == 7);
154 CHECK (mat.length() == 7);
155 CHECK (mat.prefix() ==
" catch "_expect);
156 CHECK (mat.suffix() ==
" ever "_expect);
157 CHECK (mat[0] ==
"${else}"_expect);
158 CHECK (mat[2] ==
"else"_expect);
159 CHECK (not mat[1].matched);
160 CHECK (not mat[3].matched);
161 CHECK (not mat[4].matched);
162 CHECK (not mat[5].matched);
164 input =
" catch ${else if} fever \\${can.beer} ";
165 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
166 CHECK (mat.position() == 24);
167 CHECK (mat.length() == 2);
168 CHECK (mat.prefix() ==
" catch ${else if} fever "_expect);
169 CHECK (mat.suffix() ==
"{can.beer} "_expect);
170 CHECK (mat[0] ==
"\\$"_expect);
171 CHECK (not mat[2].matched);
172 CHECK (not mat[3].matched);
173 CHECK (not mat[4].matched);
174 CHECK (not mat[5].matched);
175 CHECK (mat[1] ==
"\\$"_expect);
180 input =
"one ${two} three \\${four} ${if high} five";
183 .transform ([](smatch mat){
return mat.str(); }))
185 "${two}, \\$, ${if high}"_expect);
189 auto parser = text_template::parse (input);
190 CHECK (not isnil(parser));
191 CHECK (parser->syntax == TagSyntax::KEYID);
192 CHECK (parser->lead ==
"one "_expect);
193 CHECK (parser->key ==
"two"_expect);
196 CHECK (parser->syntax == TagSyntax::ESCAPE);
197 CHECK (parser->lead ==
" three "_expect);
198 CHECK (parser->key ==
""_expect);
201 CHECK (parser->syntax == TagSyntax::IF);
202 CHECK (parser->lead ==
"${four} "_expect);
203 CHECK (parser->key ==
"high"_expect);
205 CHECK (isnil (parser));
213 Prefix-1 ${some.key} next one is \${escaped} 214 Prefix-2 ${if cond1} active ${else} inactive ${end if 215 }Prefix-3 ${if cond2} active2${end if cond2} more 216 Prefix-4 ${for data} fixed ${embedded} 217 Pre-5 ${if nested}nested-active${ 218 else }nested-inactive${ end 219 if nested}loop-suffix${else}${end 223 CHECK (25 == actions.size());
226 CHECK (actions[ 0].val ==
"\n Prefix-1 "_expect);
227 CHECK (actions[ 0].refIDX == 0);
229 CHECK (actions[ 1].code == TextTemplate::Code::KEY);
230 CHECK (actions[ 1].val ==
"some.key"_expect);
233 CHECK (actions[ 2].val ==
" next one is "_expect);
236 CHECK (actions[ 3].val ==
"${escaped}\n Prefix-2 "_expect);
238 CHECK (actions[ 4].code == TextTemplate::Code::COND);
239 CHECK (actions[ 4].val ==
"cond1"_expect);
240 CHECK (actions[ 4].refIDX == 7 );
243 CHECK (actions[ 5].val ==
" active "_expect);
245 CHECK (actions[ 6].code == TextTemplate::Code::JUMP);
246 CHECK (actions[ 6].val ==
""_expect);
247 CHECK (actions[ 6].refIDX == 8 );
250 CHECK (actions[ 7].val ==
" inactive "_expect);
253 CHECK (actions[ 8].val ==
"Prefix-3 "_expect);
255 CHECK (actions[ 9].code == TextTemplate::Code::COND);
256 CHECK (actions[ 9].val ==
"cond2"_expect);
257 CHECK (actions[ 9].refIDX == 11 );
260 CHECK (actions[10].val ==
" active2"_expect);
263 CHECK (actions[11].val ==
" more\n Prefix-4 "_expect);
265 CHECK (actions[12].code == TextTemplate::Code::ITER);
266 CHECK (actions[12].val ==
"data"_expect);
267 CHECK (actions[12].refIDX == 23 );
270 CHECK (actions[13].val ==
" fixed "_expect);
272 CHECK (actions[14].code == TextTemplate::Code::KEY);
273 CHECK (actions[14].val ==
"embedded"_expect);
276 CHECK (actions[15].val ==
"\n Pre-5 "_expect);
278 CHECK (actions[16].code == TextTemplate::Code::COND);
279 CHECK (actions[16].val ==
"nested"_expect);
280 CHECK (actions[16].refIDX == 19 );
283 CHECK (actions[17].val ==
"nested-active"_expect);
285 CHECK (actions[18].code == TextTemplate::Code::JUMP);
286 CHECK (actions[18].val ==
""_expect);
287 CHECK (actions[18].refIDX == 20 );
290 CHECK (actions[19].val ==
"nested-inactive"_expect);
293 CHECK (actions[20].val ==
"loop-suffix"_expect);
295 CHECK (actions[21].code == TextTemplate::Code::LOOP);
296 CHECK (actions[21].val ==
""_expect);
297 CHECK (actions[21].refIDX == 12 );
299 CHECK (actions[22].code == TextTemplate::Code::JUMP);
300 CHECK (actions[22].val ==
""_expect);
301 CHECK (actions[22].refIDX == 24 );
304 CHECK (actions[23].val ==
""_expect);
307 CHECK (actions[24].val ==
" tail...\n"_expect);
308 CHECK (actions[24].refIDX == 0);
312 VERIFY_FAIL (
"TextTemplate spec without active placeholders" 315 VERIFY_FAIL (
"Tag without key: ...horror ${<placeholder> |↯|}" 327 VERIFY_FAIL (
"unqualified \"end\" without logic-keyword" 330 VERIFY_FAIL (
"Unbalanced Logic: expect ${end ?? } -- found ...horror ${end |↯|for }" 333 VERIFY_FAIL (
"Unbalanced Logic: expect ${end for free} -- found ... horror ${end |↯|if }" 336 VERIFY_FAIL (
"Unbalanced Logic: expect ${end for free} -- found ... yet ${end |↯|for me}" 339 VERIFY_FAIL (
"Conflicting ... precipitous ${else} ⟷ ... callous |↯|${else}" 342 VERIFY_FAIL (
"Unclosed Logic tags: |↯|${end if sleazy} missing" 345 VERIFY_FAIL (
"Unclosed Logic tags: |↯|${end for horror} missing" 355 string wonder =
"${a} / ${b} = (${a} + ${b})/${a} ≕ ${phi}";
357 CHECK (join(temple.keys()) ==
"a, b, a, b, a, phi"_expect);
359 auto insta = temple.submit (
string{
"phi=Φ, b=b, a=a"});
360 CHECK (not isnil(insta));
361 CHECK (join(insta,
"⁐") ==
"⁐a⁐ / ⁐b⁐ = (⁐a⁐ + ⁐b⁐)/⁐a⁐ ≕ ⁐Φ⁐"_expect);
363 CHECK (temple.render(
"phi=Φ,a=μ,b=ν") ==
"μ / ν = (μ + ν)/μ ≕ Φ"_expect );
364 CHECK (temple.render(
"phi=schmuh,a=8,b=5") ==
"8 / 5 = (8 + 5)/8 ≕ schmuh"_expect);
365 CHECK (temple.render(
"phi=1.6180,a=55,b=34") ==
"55 / 34 = (55 + 34)/55 ≕ 1.6180"_expect);
376 TextTemplate t1{
"Value ${if val}= ${val} ${else}missing${endif}..."};
378 CHECK (t1.render(
"val=55") ==
"Value = 55 ..."_expect );
379 CHECK (t1.render(
"val=\"\"") ==
"Value missing..."_expect);
380 CHECK (t1.render(
"val=\" \"") ==
"Value = ..."_expect );
381 CHECK (t1.render(
"val=false") ==
"Value missing..."_expect);
382 CHECK (t1.render(
"val=NO" ) ==
"Value missing..."_expect);
383 CHECK (t1.render(
"val= 0 " ) ==
"Value missing..."_expect);
384 CHECK (t1.render(
"val=true") ==
"Value = true ..."_expect);
385 CHECK (t1.render(
"vol=high") ==
"Value missing..."_expect);
388 TextTemplate t2{
"Solution${if val} is ${val} ${endif val}..."};
389 CHECK (t2.render(
"val=42") ==
"Solution is 42 ..."_expect );
390 CHECK (t2.render(
"nil=42") ==
"Solution..."_expect );
393 TextTemplate t3{
" 1 ${if a} 2 ${if b} 3 ${else} ${b} ${endif b} 4 ${else}${if a} 5 ${else} ${a} ${endif a}${endif a} 6 "};
394 CHECK (t3.render(
"a=2,b=3") ==
" 1 2 3 4 6 "_expect );
395 CHECK (t3.render(
"a=2,b=0") ==
" 1 2 0 4 6 "_expect );
396 CHECK (t3.render(
"a=0,b=3") ==
" 1 0 6 "_expect );
397 CHECK (t3.render(
"a=0,b=0") ==
" 1 0 6 "_expect );
415 TextTemplate t1{
"▶${for i} ${x} ▷${else} ∅${end for} ◇ ${i} ▶"};
417 CHECK (t1.render(
"i=\"1,2,3\", i.1.x=3, i.2.x=5, i.3.x=8 ") ==
"▶ 3 ▷ 5 ▷ 8 ▷ ◇ 1,2,3 ▶"_expect );
418 CHECK (t1.render(
"i=\"3,1,2\", i.1.x=3, i.2.x=5, i.3.x=8 ") ==
"▶ 8 ▷ 3 ▷ 5 ▷ ◇ 3,1,2 ▶"_expect );
419 CHECK (t1.render(
"i=\"3,2,3\", i.1.x=3, i.2.x=5, i.3.x=8 ") ==
"▶ 8 ▷ 5 ▷ 8 ▷ ◇ 3,2,3 ▶"_expect );
420 CHECK (t1.render(
"i=\"3,2,1\", i.2.x=5, i.3.x=8 ") ==
"▶ 8 ▷ 5 ▷ ▷ ◇ 3,2,1 ▶"_expect );
421 CHECK (t1.render(
"i=\"3,2,1\", x=↯, i.2.x=5, i.3.x=8 ") ==
"▶ 8 ▷ 5 ▷ ↯ ▷ ◇ 3,2,1 ▶"_expect );
422 CHECK (t1.render(
"i=\"p,q,r\", x=↯, i.q.x=5, i.3.x=8 ") ==
"▶ ↯ ▷ 5 ▷ ↯ ▷ ◇ p,q,r ▶"_expect );
423 CHECK (t1.render(
"i= 0 , x=↯, i.q.x=5, i.3.x=8 ") ==
"▶ ∅ ◇ 0 ▶"_expect );
424 CHECK (t1.render(
" x=↯, i.q.x=5, i.3.x=8 ") ==
"▶ ∅ ◇ ▶"_expect );
427 TextTemplate t2{
"▶${for i}${if x}${for j}${x}▷${else}${x}●${end for j}${end if x} 🔁 ${end for i} ▶"};
429 CHECK (t2.render(
"i=\"1,2\",j=\"1,2\", x=1 , i.1.j.1.x=11, i.1.j.2.x=12, i.2.j.1.x=21, i.2.j.2.x=22") ==
"▶11▷12▷ 🔁 21▷22▷ 🔁 ▶"_expect );
430 CHECK (t2.render(
"i=\"1,2\",j=\"1,2\", i.1.x=1, i.1.j.1.x=11, i.1.j.2.x=12, i.2.j.1.x=21, i.2.j.2.x=22") ==
"▶11▷12▷ 🔁 🔁 ▶"_expect );
431 CHECK (t2.render(
"i=\"1,2\" , x=00 , i.1.j.1.x=11, i.1.j.2.x=12, i.2.j.1.x=21, i.2.j.2.x=22") ==
"▶00● 🔁 00● 🔁 ▶"_expect );
432 CHECK (t2.render(
"i=\"1,2\" , x=00 , i.1.x =10, i.2.x =20, ") ==
"▶10● 🔁 20● 🔁 ▶"_expect );
433 CHECK (t2.render(
" j=\"1,2\" ") ==
"▶ ▶"_expect );
434 CHECK (t2.render(
" ") ==
"▶ ▶"_expect );
453 CHECK (meta::typeStr(binding) ==
"text_template::DataSource<map<string, string>, void>"_expect );
454 CHECK ( binding.contains(
"a"));
455 CHECK (not binding.contains(
"b"));
456 CHECK (binding.retrieveContent(
"a") ==
"5"_expect );
457 CHECK (binding.retrieveContent(
"i") ==
"p,q,r"_expect );
458 CHECK (binding.retrieveContent(
"i.q.aa") ==
"222"_expect );
459 CHECK (not binding.isSubScope());
461 auto it = binding.getSequence(
"i");
463 CHECK (*it ==
"i.p."_expect );
464 CHECK (meta::typeStr(it) ==
"IterExplorer<IterableDecorator<CheckedCore<iter_explorer::Transformer<iter_explorer::BaseAdapter<RegexSearchIter>, string> > > >"_expect );
466 auto subBind = binding.openContext(it);
467 CHECK (subBind.isSubScope());
468 CHECK ((meta::is_same<decltype(binding),decltype(subBind)>()));
469 CHECK ( subBind.contains(
"a"));
470 CHECK (not subBind.contains(
"b"));
471 CHECK (not subBind.contains(
"aa"));
472 CHECK ( subBind.contains(
"i"));
473 CHECK (subBind.retrieveContent(
"i") ==
"p,q,r"_expect );
474 CHECK (subBind.retrieveContent(
"a") ==
"11"_expect );
478 CHECK (*it ==
"i.q."_expect );
481 CHECK (subBind.retrieveContent(
"a") ==
"11"_expect );
483 subBind = binding.openContext(it);
484 CHECK (subBind.isSubScope());
485 CHECK (subBind.contains(
"a"));
486 CHECK (subBind.contains(
"aa"));
487 CHECK (subBind.retrieveContent(
"a") ==
"22"_expect );
488 CHECK (subBind.retrieveContent(
"aa") ==
"222"_expect);
489 CHECK (subBind.retrieveContent(
"i.p.a") ==
"11"_expect );
490 CHECK (subBind.retrieveContent(
"i.q.a") ==
"22"_expect );
494 CHECK (*it ==
"i.r."_expect );
496 subBind = binding.openContext(it);
497 CHECK ( subBind.contains(
"a"));
498 CHECK (not subBind.contains(
"aa"));
499 CHECK (subBind.retrieveContent(
"a") ==
"5"_expect );
500 CHECK (subBind.retrieveContent(
"i.p.a") ==
"11"_expect );
501 CHECK (subBind.retrieveContent(
"i.q.a") ==
"22"_expect );
522 auto root = MakeRec()
539 CHECK (meta::typeStr(binding) ==
"text_template::DataSource<GenNode, void>"_expect );
540 CHECK ( binding.contains(
"a"));
541 CHECK (not binding.contains(
"b"));
542 CHECK (binding.retrieveContent(
"a") ==
"5"_expect );
543 CHECK (binding.retrieveContent(
"i") ==
"{|{a=11}, {a=22, aa=222}, {}}"_expect );
544 CHECK (not binding.isSubScope());
546 auto it = binding.getSequence(
"i");
548 CHECK (renderCompact(*it) ==
"{a=11}");
549 CHECK (*it == root.data.get<Rec>().get(
"i").data.get<Rec>().child(0));
551 auto subBind = binding.openContext(it);
552 CHECK (subBind.isSubScope());
553 CHECK ((meta::is_same<decltype(binding),decltype(subBind)>()));
554 CHECK ( subBind.contains(
"a"));
555 CHECK (not subBind.contains(
"b"));
556 CHECK (not subBind.contains(
"aa"));
557 CHECK ( subBind.contains(
"i"));
558 CHECK (subBind.retrieveContent(
"i") ==
"{|{a=11}, {a=22, aa=222}, {}}"_expect );
559 CHECK (subBind.retrieveContent(
"a") ==
"11"_expect );
563 CHECK (renderCompact(*it) ==
"{a=22, aa=222}");
564 CHECK (subBind.retrieveContent(
"a") ==
"11"_expect );
566 subBind = binding.openContext(it);
567 CHECK (subBind.isSubScope());
568 CHECK (subBind.contains(
"a"));
569 CHECK (subBind.contains(
"aa"));
570 CHECK (subBind.retrieveContent(
"a") ==
"22"_expect );
571 CHECK (subBind.retrieveContent(
"aa") ==
"222"_expect);
575 CHECK (renderCompact(*it) ==
"{}");
577 subBind = binding.openContext(it);
578 CHECK ( subBind.contains(
"a"));
579 CHECK (not subBind.contains(
"aa"));
580 CHECK (subBind.retrieveContent(
"a") ==
"5"_expect );
587 TextTemplate tt{
"${for i}a=${a} ${if aa}and aa=${aa} ${endif}${endfor}."};
588 CHECK (tt.render(root) ==
"a=11 a=22 and aa=222 a=5 ."_expect);
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
void verify_conditional()
void verify_ETD_binding()
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
Text template substitution engine.
A front-end for using printf-style formatting.
Implementation namespace for support and library code.
void verify_instantiation()
A minimalistic text templating engine with flexible data binding.
Simplistic test class runner.
void verify_Map_binding()
A collection of frequently used helper functions to support unit testing.
static ActionSeq compile(string const &)
wrapped regex iterator to allow usage in foreach loops
static string apply(string spec, DAT const &data)
one-shot shorthand: compile a template and apply it to the given data
A complement to allow instantiation of a TextTemplate with ETD data.
object-like record of data.
generic data element node within a tree
#define VERIFY_FAIL(FAILURE_MSG, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises a std::exception, which additionally contains some FAI...