diff --git a/src/lib/text-template.hpp b/src/lib/text-template.hpp index bfdc40ca2..e50abe568 100644 --- a/src/lib/text-template.hpp +++ b/src/lib/text-template.hpp @@ -186,11 +186,15 @@ namespace lib { if ("for" == mat[4]) tag.syntax = mat[3].matched? TagSyntax::END_FOR : TagSyntax::FOR; else - throw error::Logic(_Fmt{"unexpected keyword \"%s\""} % mat[4]); + throw error::Logic{_Fmt{"unexpected keyword \"%s\""} % mat[4]}; } else if (mat[2].matched) tag.syntax = TagSyntax::ELSE; + else + if ("end" == mat[5]) + throw error::Invalid{_Fmt{"unqualified \"end\" without logic-keyword:" + " ...%s${end |↯|}"} % tag.lead}; else tag.syntax = TagSyntax::KEYID; } @@ -358,6 +362,13 @@ namespace lib { auto abbrev = [&](auto s){ return s.length()<16? s : s.substr(s.length()-15); }; // (shorten lead display to 15 chars) // Syntax / consistency checks... + auto __requireKey = [&](string descr) + { + if (isnil (parseIter->key)) + throw error::Invalid{_Fmt{"Tag without key: ...%s${%s |↯|}"} + % abbrev(lead()) % descr + }; + }; auto __checkBalanced = [&](Clause c) { if (not scopeMatch(c)) @@ -400,10 +411,12 @@ namespace lib { addLead(); break; case TagSyntax::KEYID: + __requireKey(""); addLead(); addCode(KEY); break; case TagSyntax::IF: + __requireKey("if "); addLead(); openScope(IF); addCode(COND); @@ -418,6 +431,7 @@ namespace lib { closeScope(); break; case TagSyntax::FOR: + __requireKey("for "); addLead(); openScope(FOR); addCode(ITER); diff --git a/tests/library/text-template-test.cpp b/tests/library/text-template-test.cpp index 3078e0732..8bf8302c4 100644 --- a/tests/library/text-template-test.cpp +++ b/tests/library/text-template-test.cpp @@ -317,6 +317,38 @@ for} tail... CHECK (actions[24].code == TextTemplate::Code::TEXT); // a final static segment after the last active tag CHECK (actions[24].val == " tail...\n"_expect); CHECK (actions[24].refIDX == 0); + + + + VERIFY_FAIL ("TextTemplate spec without active placeholders" + , TextTemplate::compile("horror boredom")); + + VERIFY_FAIL ("Tag without key: ...horror ${ |↯|}" + , TextTemplate::compile("horror ${ } vacui")); + + VERIFY_FAIL (" ...horror ${if |↯|}" + , TextTemplate::compile("horror ${if} late")); + + VERIFY_FAIL (" ...horror ${for |↯|}" + , TextTemplate::compile("horror ${for} all")); + + VERIFY_FAIL ("Misplaced ...horror |↯|${else}" + , TextTemplate::compile("horror ${else} deaf")); + + VERIFY_FAIL ("unqualified \"end\" without logic-keyword" + , TextTemplate::compile("horror without ${end}")); + + VERIFY_FAIL ("Unbalanced Logic: expect ${end ?? } -- found ...horror ${end |↯|for }" + , TextTemplate::compile("horror ${end for} ever")); + + VERIFY_FAIL ("Unbalanced Logic: expect ${end for free} -- found ...${end |↯|if }" + , TextTemplate::compile("horror ${for free}${end if}")); + + VERIFY_FAIL ("Unbalanced Logic: expect ${end for free} -- found ... yet ${end |↯|for me}" + , TextTemplate::compile("${if wee} horror ${for free} yet ${end for me}")); + + VERIFY_FAIL ("Conflicting ... precipitous ${else} ⟷ ... callous |↯|${else}" + , TextTemplate::compile("${if smarmy} precipitous ${else} callous ${else} horror")); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 0809d0e1b..25263a6d2 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -113257,16 +113257,16 @@ std::cout << tmpl.render({"what", "World"}) << s - - + + - - + + - - + + @@ -113392,11 +113392,11 @@ std::cout << tmpl.render({"what", "World"}) << s - - + + - - + + @@ -113406,8 +113406,10 @@ std::cout << tmpl.render({"what", "World"}) << s - - + + + +