37 using std::regex_search;
49 using MapS = std::map<string, string>;
50 using LERR_(ITER_EXHAUST);
52 using text_template::ACCEPT_MARKUP;
53 using text_template::TagSyntax;
87 MapS snaps{{
"whatever",
"cruel world"}
88 ,{
"greeting",
"farewell"}};
90 ==
"farewell cruel world ↯"_expect);
108 CHECK (not regex_search (input, mat, ACCEPT_MARKUP));
110 input =
" Hallelujah ";
111 CHECK (not regex_search (input, mat, ACCEPT_MARKUP));
113 input =
" stale${beer}forever";
114 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
115 CHECK (mat.position() == 6);
116 CHECK (mat.length() == 7);
117 CHECK (mat.prefix() ==
" stale"_expect);
118 CHECK (mat.suffix() ==
"forever"_expect);
119 CHECK (mat[0] ==
"${beer}"_expect);
120 CHECK (not mat[1].matched);
121 CHECK (not mat[2].matched);
122 CHECK (not mat[3].matched);
123 CHECK (not mat[4].matched);
124 CHECK (mat[5] ==
"beer"_expect);
126 input =
" watch ${for stale}${beer} whatever ";
127 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
128 CHECK (mat.position() == 7);
129 CHECK (mat.length() == 12);
130 CHECK (mat.prefix() ==
" watch "_expect);
131 CHECK (mat.suffix() ==
"${beer} whatever "_expect);
132 CHECK (mat[0] ==
"${for stale}"_expect);
133 CHECK (not mat[2].matched);
134 CHECK (not mat[3].matched);
135 CHECK (mat[4] ==
"for"_expect);
136 CHECK (mat[5] ==
"stale"_expect);
138 input =
" work ${ end if beer \t } however ";
139 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
140 CHECK (mat.position() == 6);
141 CHECK (mat.length() == 19);
142 CHECK (mat.prefix() ==
" work "_expect);
143 CHECK (mat.suffix() ==
" however "_expect);
144 CHECK (mat[0] ==
"${ end if beer \t }"_expect);
145 CHECK (mat[3] ==
"end "_expect);
146 CHECK (mat[4] ==
"if"_expect);
147 CHECK (mat[5] ==
"beer"_expect);
149 input =
" catch ${endgame stale}${endfor brown.beer} ever ";
150 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
151 CHECK (mat.position() == 23);
152 CHECK (mat.length() == 20);
153 CHECK (mat.prefix() ==
" catch ${endgame stale}"_expect);
154 CHECK (mat.suffix() ==
" ever "_expect);
155 CHECK (mat[0] ==
"${endfor brown.beer}"_expect);
156 CHECK (mat[3] ==
"end"_expect);
157 CHECK (mat[4] ==
"for"_expect);
158 CHECK (mat[5] ==
"brown.beer"_expect);
160 input =
" catch ${else} ever ";
161 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
162 CHECK (mat.position() == 7);
163 CHECK (mat.length() == 7);
164 CHECK (mat.prefix() ==
" catch "_expect);
165 CHECK (mat.suffix() ==
" ever "_expect);
166 CHECK (mat[0] ==
"${else}"_expect);
167 CHECK (mat[2] ==
"else"_expect);
168 CHECK (not mat[1].matched);
169 CHECK (not mat[3].matched);
170 CHECK (not mat[4].matched);
171 CHECK (not mat[5].matched);
173 input =
" catch ${else if} fever \\${can.beer} ";
174 CHECK (regex_search (input, mat, ACCEPT_MARKUP));
175 CHECK (mat.position() == 24);
176 CHECK (mat.length() == 2);
177 CHECK (mat.prefix() ==
" catch ${else if} fever "_expect);
178 CHECK (mat.suffix() ==
"{can.beer} "_expect);
179 CHECK (mat[0] ==
"\\$"_expect);
180 CHECK (not mat[2].matched);
181 CHECK (not mat[3].matched);
182 CHECK (not mat[4].matched);
183 CHECK (not mat[5].matched);
184 CHECK (mat[1] ==
"\\$"_expect);
189 input =
"one ${two} three \\${four} ${if high} five";
192 .transform ([](smatch mat){
return mat.str(); }))
194 "${two}, \\$, ${if high}"_expect);
198 auto parser = text_template::parse (input);
199 CHECK (not isnil(parser));
200 CHECK (parser->syntax == TagSyntax::KEYID);
201 CHECK (parser->lead ==
"one "_expect);
202 CHECK (parser->key ==
"two"_expect);
205 CHECK (parser->syntax == TagSyntax::ESCAPE);
206 CHECK (parser->lead ==
" three "_expect);
207 CHECK (parser->key ==
""_expect);
210 CHECK (parser->syntax == TagSyntax::IF);
211 CHECK (parser->lead ==
"${four} "_expect);
212 CHECK (parser->key ==
"high"_expect);
214 CHECK (isnil (parser));
222 Prefix-1 ${some.key} next one is \${escaped} 223 Prefix-2 ${if cond1} active ${else} inactive ${end if 224 }Prefix-3 ${if cond2} active2${end if cond2} more 225 Prefix-4 ${for data} fixed ${embedded} 226 Pre-5 ${if nested}nested-active${ 227 else }nested-inactive${ end 228 if nested}loop-suffix${else}${end 232 CHECK (25 == actions.size());
235 CHECK (actions[ 0].val ==
"\n Prefix-1 "_expect);
236 CHECK (actions[ 0].refIDX == 0);
238 CHECK (actions[ 1].code == TextTemplate::Code::KEY);
239 CHECK (actions[ 1].val ==
"some.key"_expect);
242 CHECK (actions[ 2].val ==
" next one is "_expect);
245 CHECK (actions[ 3].val ==
"${escaped}\n Prefix-2 "_expect);
247 CHECK (actions[ 4].code == TextTemplate::Code::COND);
248 CHECK (actions[ 4].val ==
"cond1"_expect);
249 CHECK (actions[ 4].refIDX == 7 );
252 CHECK (actions[ 5].val ==
" active "_expect);
254 CHECK (actions[ 6].code == TextTemplate::Code::JUMP);
255 CHECK (actions[ 6].val ==
""_expect);
256 CHECK (actions[ 6].refIDX == 8 );
259 CHECK (actions[ 7].val ==
" inactive "_expect);
262 CHECK (actions[ 8].val ==
"Prefix-3 "_expect);
264 CHECK (actions[ 9].code == TextTemplate::Code::COND);
265 CHECK (actions[ 9].val ==
"cond2"_expect);
266 CHECK (actions[ 9].refIDX == 11 );
269 CHECK (actions[10].val ==
" active2"_expect);
272 CHECK (actions[11].val ==
" more\n Prefix-4 "_expect);
274 CHECK (actions[12].code == TextTemplate::Code::ITER);
275 CHECK (actions[12].val ==
"data"_expect);
276 CHECK (actions[12].refIDX == 23 );
279 CHECK (actions[13].val ==
" fixed "_expect);
281 CHECK (actions[14].code == TextTemplate::Code::KEY);
282 CHECK (actions[14].val ==
"embedded"_expect);
285 CHECK (actions[15].val ==
"\n Pre-5 "_expect);
287 CHECK (actions[16].code == TextTemplate::Code::COND);
288 CHECK (actions[16].val ==
"nested"_expect);
289 CHECK (actions[16].refIDX == 19 );
292 CHECK (actions[17].val ==
"nested-active"_expect);
294 CHECK (actions[18].code == TextTemplate::Code::JUMP);
295 CHECK (actions[18].val ==
""_expect);
296 CHECK (actions[18].refIDX == 20 );
299 CHECK (actions[19].val ==
"nested-inactive"_expect);
302 CHECK (actions[20].val ==
"loop-suffix"_expect);
304 CHECK (actions[21].code == TextTemplate::Code::LOOP);
305 CHECK (actions[21].val ==
""_expect);
306 CHECK (actions[21].refIDX == 12 );
308 CHECK (actions[22].code == TextTemplate::Code::JUMP);
309 CHECK (actions[22].val ==
""_expect);
310 CHECK (actions[22].refIDX == 24 );
313 CHECK (actions[23].val ==
""_expect);
316 CHECK (actions[24].val ==
" tail...\n"_expect);
317 CHECK (actions[24].refIDX == 0);
321 VERIFY_FAIL (
"TextTemplate spec without active placeholders" 324 VERIFY_FAIL (
"Tag without key: ...horror ${<placeholder> |↯|}" 336 VERIFY_FAIL (
"unqualified \"end\" without logic-keyword" 339 VERIFY_FAIL (
"Unbalanced Logic: expect ${end ?? } -- found ...horror ${end |↯|for }" 342 VERIFY_FAIL (
"Unbalanced Logic: expect ${end for free} -- found ... horror ${end |↯|if }" 345 VERIFY_FAIL (
"Unbalanced Logic: expect ${end for free} -- found ... yet ${end |↯|for me}" 348 VERIFY_FAIL (
"Conflicting ... precipitous ${else} ⟷ ... callous |↯|${else}" 351 VERIFY_FAIL (
"Unclosed Logic tags: |↯|${end if sleazy} missing" 354 VERIFY_FAIL (
"Unclosed Logic tags: |↯|${end for horror} missing" 364 string wonder =
"${a} / ${b} = (${a} + ${b})/${a} ≕ ${phi}";
366 CHECK (join(temple.keys()) ==
"a, b, a, b, a, phi"_expect);
368 auto insta = temple.submit (
string{
"phi=Φ, b=b, a=a"});
369 CHECK (not isnil(insta));
370 CHECK (join(insta,
"⁐") ==
"⁐a⁐ / ⁐b⁐ = (⁐a⁐ + ⁐b⁐)/⁐a⁐ ≕ ⁐Φ⁐"_expect);
372 CHECK (temple.render(
"phi=Φ,a=μ,b=ν") ==
"μ / ν = (μ + ν)/μ ≕ Φ"_expect );
373 CHECK (temple.render(
"phi=schmuh,a=8,b=5") ==
"8 / 5 = (8 + 5)/8 ≕ schmuh"_expect);
374 CHECK (temple.render(
"phi=1.6180,a=55,b=34") ==
"55 / 34 = (55 + 34)/55 ≕ 1.6180"_expect);
385 TextTemplate t1{
"Value ${if val}= ${val} ${else}missing${endif}..."};
387 CHECK (t1.render(
"val=55") ==
"Value = 55 ..."_expect );
388 CHECK (t1.render(
"val=\"\"") ==
"Value missing..."_expect);
389 CHECK (t1.render(
"val=\" \"") ==
"Value = ..."_expect );
390 CHECK (t1.render(
"val=false") ==
"Value missing..."_expect);
391 CHECK (t1.render(
"val=NO" ) ==
"Value missing..."_expect);
392 CHECK (t1.render(
"val= 0 " ) ==
"Value missing..."_expect);
393 CHECK (t1.render(
"val=true") ==
"Value = true ..."_expect);
394 CHECK (t1.render(
"vol=high") ==
"Value missing..."_expect);
397 TextTemplate t2{
"Solution${if val} is ${val} ${endif val}..."};
398 CHECK (t2.render(
"val=42") ==
"Solution is 42 ..."_expect );
399 CHECK (t2.render(
"nil=42") ==
"Solution..."_expect );
402 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 "};
403 CHECK (t3.render(
"a=2,b=3") ==
" 1 2 3 4 6 "_expect );
404 CHECK (t3.render(
"a=2,b=0") ==
" 1 2 0 4 6 "_expect );
405 CHECK (t3.render(
"a=0,b=3") ==
" 1 0 6 "_expect );
406 CHECK (t3.render(
"a=0,b=0") ==
" 1 0 6 "_expect );
424 TextTemplate t1{
"▶${for i} ${x} ▷${else} ∅${end for} ◇ ${i} ▶"};
426 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 );
427 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 );
428 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 );
429 CHECK (t1.render(
"i=\"3,2,1\", i.2.x=5, i.3.x=8 ") ==
"▶ 8 ▷ 5 ▷ ▷ ◇ 3,2,1 ▶"_expect );
430 CHECK (t1.render(
"i=\"3,2,1\", x=↯, i.2.x=5, i.3.x=8 ") ==
"▶ 8 ▷ 5 ▷ ↯ ▷ ◇ 3,2,1 ▶"_expect );
431 CHECK (t1.render(
"i=\"p,q,r\", x=↯, i.q.x=5, i.3.x=8 ") ==
"▶ ↯ ▷ 5 ▷ ↯ ▷ ◇ p,q,r ▶"_expect );
432 CHECK (t1.render(
"i= 0 , x=↯, i.q.x=5, i.3.x=8 ") ==
"▶ ∅ ◇ 0 ▶"_expect );
433 CHECK (t1.render(
" x=↯, i.q.x=5, i.3.x=8 ") ==
"▶ ∅ ◇ ▶"_expect );
436 TextTemplate t2{
"▶${for i}${if x}${for j}${x}▷${else}${x}●${end for j}${end if x} 🔁 ${end for i} ▶"};
438 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 );
439 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 );
440 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 );
441 CHECK (t2.render(
"i=\"1,2\" , x=00 , i.1.x =10, i.2.x =20, ") ==
"▶10● 🔁 20● 🔁 ▶"_expect );
442 CHECK (t2.render(
" j=\"1,2\" ") ==
"▶ ▶"_expect );
443 CHECK (t2.render(
" ") ==
"▶ ▶"_expect );
462 CHECK (meta::typeStr(binding) ==
"text_template::DataSource<map<string, string>, void>"_expect );
463 CHECK ( binding.contains(
"a"));
464 CHECK (not binding.contains(
"b"));
465 CHECK (binding.retrieveContent(
"a") ==
"5"_expect );
466 CHECK (binding.retrieveContent(
"i") ==
"p,q,r"_expect );
467 CHECK (binding.retrieveContent(
"i.q.aa") ==
"222"_expect );
468 CHECK (not binding.isSubScope());
470 auto it = binding.getSequence(
"i");
472 CHECK (*it ==
"i.p."_expect );
473 CHECK (meta::typeStr(it) ==
"IterExplorer<IterableDecorator<string, CheckedCore<iter_explorer::Transformer<iter_explorer::BaseAdapter<RegexSearchIter>, string> > > >"_expect );
475 auto subBind = binding.openContext(it);
476 CHECK (subBind.isSubScope());
477 CHECK ((meta::is_same<decltype(binding),decltype(subBind)>()));
478 CHECK ( subBind.contains(
"a"));
479 CHECK (not subBind.contains(
"b"));
480 CHECK (not subBind.contains(
"aa"));
481 CHECK ( subBind.contains(
"i"));
482 CHECK (subBind.retrieveContent(
"i") ==
"p,q,r"_expect );
483 CHECK (subBind.retrieveContent(
"a") ==
"11"_expect );
487 CHECK (*it ==
"i.q."_expect );
490 CHECK (subBind.retrieveContent(
"a") ==
"11"_expect );
492 subBind = binding.openContext(it);
493 CHECK (subBind.isSubScope());
494 CHECK (subBind.contains(
"a"));
495 CHECK (subBind.contains(
"aa"));
496 CHECK (subBind.retrieveContent(
"a") ==
"22"_expect );
497 CHECK (subBind.retrieveContent(
"aa") ==
"222"_expect);
498 CHECK (subBind.retrieveContent(
"i.p.a") ==
"11"_expect );
499 CHECK (subBind.retrieveContent(
"i.q.a") ==
"22"_expect );
503 CHECK (*it ==
"i.r."_expect );
505 subBind = binding.openContext(it);
506 CHECK ( subBind.contains(
"a"));
507 CHECK (not subBind.contains(
"aa"));
508 CHECK (subBind.retrieveContent(
"a") ==
"5"_expect );
509 CHECK (subBind.retrieveContent(
"i.p.a") ==
"11"_expect );
510 CHECK (subBind.retrieveContent(
"i.q.a") ==
"22"_expect );
531 auto root = MakeRec()
548 CHECK (meta::typeStr(binding) ==
"text_template::DataSource<GenNode, void>"_expect );
549 CHECK ( binding.contains(
"a"));
550 CHECK (not binding.contains(
"b"));
551 CHECK (binding.retrieveContent(
"a") ==
"5"_expect );
552 CHECK (binding.retrieveContent(
"i") ==
"{|{a=11}, {a=22, aa=222}, {}}"_expect );
553 CHECK (not binding.isSubScope());
555 auto it = binding.getSequence(
"i");
557 CHECK (renderCompact(*it) ==
"{a=11}");
558 CHECK (*it == root.data.get<Rec>().get(
"i").data.get<Rec>().child(0));
560 auto subBind = binding.openContext(it);
561 CHECK (subBind.isSubScope());
562 CHECK ((meta::is_same<decltype(binding),decltype(subBind)>()));
563 CHECK ( subBind.contains(
"a"));
564 CHECK (not subBind.contains(
"b"));
565 CHECK (not subBind.contains(
"aa"));
566 CHECK ( subBind.contains(
"i"));
567 CHECK (subBind.retrieveContent(
"i") ==
"{|{a=11}, {a=22, aa=222}, {}}"_expect );
568 CHECK (subBind.retrieveContent(
"a") ==
"11"_expect );
572 CHECK (renderCompact(*it) ==
"{a=22, aa=222}");
573 CHECK (subBind.retrieveContent(
"a") ==
"11"_expect );
575 subBind = binding.openContext(it);
576 CHECK (subBind.isSubScope());
577 CHECK (subBind.contains(
"a"));
578 CHECK (subBind.contains(
"aa"));
579 CHECK (subBind.retrieveContent(
"a") ==
"22"_expect );
580 CHECK (subBind.retrieveContent(
"aa") ==
"222"_expect);
584 CHECK (renderCompact(*it) ==
"{}");
586 subBind = binding.openContext(it);
587 CHECK ( subBind.contains(
"a"));
588 CHECK (not subBind.contains(
"aa"));
589 CHECK (subBind.retrieveContent(
"a") ==
"5"_expect );
596 TextTemplate tt{
"${for i}a=${a} ${if aa}and aa=${aa} ${endif}${endfor}."};
597 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.
Simple 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...