diff --git a/src/lib/stat/regex.hpp b/src/lib/regex.hpp similarity index 91% rename from src/lib/stat/regex.hpp rename to src/lib/regex.hpp index 59ecb883e..301056c5c 100644 --- a/src/lib/stat/regex.hpp +++ b/src/lib/regex.hpp @@ -23,13 +23,11 @@ /** @file regex.hpp ** Convenience wrappers and helpers for dealing with regular expressions. - ** - ** @todo 3/2024 should be with the generic utils, might be a Lumiera Forward Iterator */ -#ifndef LIB_STAT_REGEX_H -#define LIB_STAT_REGEX_H +#ifndef LIB_REGEX_H +#define LIB_REGEX_H #include "lib/iter-adapter.hpp" @@ -65,5 +63,11 @@ namespace util { ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (RegexSearchIter); }; -} // namespace util +}// namespace util + +namespace lib { + using std::regex; + using std::smatch; + using std::string; +}// namespace lib #endif/*LIB_STAT_REGEX_H*/ diff --git a/src/lib/stat/csv.hpp b/src/lib/stat/csv.hpp index dbfdaaabe..bad863942 100644 --- a/src/lib/stat/csv.hpp +++ b/src/lib/stat/csv.hpp @@ -51,7 +51,7 @@ #include "lib/null-value.hpp" #include "lib/format-string.hpp" #include "lib/format-obj.hpp" -#include "lib/stat/regex.hpp" +#include "lib/regex.hpp" #include #include diff --git a/src/lib/text-template.hpp b/src/lib/text-template.hpp index df3f3d420..e80352ea2 100644 --- a/src/lib/text-template.hpp +++ b/src/lib/text-template.hpp @@ -99,12 +99,12 @@ #include "lib/error.hpp" #include "lib/nocopy.hpp" +#include "lib/iter-index.hpp" #include "lib/iter-explorer.hpp" #include "lib/format-util.hpp" +#include "lib/regex.hpp" #include "lib/util.hpp" -//#include -//#include #include #include #include @@ -124,6 +124,20 @@ namespace lib { /** shorthand for an »iter-explorer« build from some source X */ template using ExploreIter = decltype (lib::explore (std::declval())); + + const string MATCH_SINGLE_KEY = "[A-Za-z_]+\\w*"; + const string MATCH_KEY_PATH = MATCH_SINGLE_KEY+"(?:\\."+MATCH_SINGLE_KEY+")*"; + const string MATCH_LOGIC_TOK = "(?:if|for)"; + const string MATCH_END_TOK = "(?:end\\s*)"; + const string MATCH_ELSE_TOK = "else"; + const string MATCH_SYNTAX = "("+MATCH_END_TOK+")?(?:("+MATCH_LOGIC_TOK+")\\s+)?("+MATCH_KEY_PATH+")|("+MATCH_ELSE_TOK+")"; + const string MATCH_FIELD = "\\$\\{(?:"+MATCH_SYNTAX+")\\}"; + const string MATCH_ESCAPE = R"~((\\\$))~"; + + const regex ACCEPT_MARKUP { MATCH_FIELD+"|"+MATCH_ESCAPE + , regex::optimize + }; + // Sub-Matches: 1 = END; 2 = LOGIC; 3 = KEY; 4 = ELSE; 5 = ESCAPE } @@ -176,7 +190,7 @@ namespace lib { template class InstanceCore { - using ActionIter = ExploreIter; + using ActionIter = IterIndex; using DataCtxIter = typename SRC::Iter; using NestedCtx = std::pair; using CtxStack = std::stack>; @@ -194,6 +208,7 @@ namespace lib { void iterNext(); void instantiateNext(); + StrView getContent(string key); }; template @@ -225,14 +240,33 @@ namespace lib { "unable to bind this data source " "for TextTemplate instantiation"); }; - + + using MapS = std::map; + template<> - struct TextTemplate::DataSource> + struct TextTemplate::DataSource { + MapS* data_; using Iter = std::string_view; + + bool + contains (string key) + { + return util::contains (*data_, key); + } + + string const& + retrieveContent (string key) + { + return (*data_)[key]; + } }; + + + /* ======= implementation of the instantiation state ======= */ + template TextTemplate::InstanceCore::InstanceCore (TextTemplate::ActionSeq const& actions, SRC s) : dataSrc_{s} @@ -278,6 +312,14 @@ namespace lib { : StrView{}; } + template + inline StrView + TextTemplate::InstanceCore::getContent(string key) + { + static StrView nil{""}; + return dataSrc_.contains(key)? dataSrc_.retrieveContent(key) : nil; + } + /** @@ -288,13 +330,13 @@ namespace lib { */ template inline StrView - TextTemplate::Action::instantiate (InstanceCore&) const + TextTemplate::Action::instantiate (InstanceCore& core) const { switch (code) { case TEXT: return val; case KEY: - return ""; + return core.getContent (val); case COND: return ""; case JUMP: diff --git a/tests/library/text-template-test.cpp b/tests/library/text-template-test.cpp index 1f45066fa..69262520e 100644 --- a/tests/library/text-template-test.cpp +++ b/tests/library/text-template-test.cpp @@ -37,6 +37,8 @@ #include //using std::array; +using std::regex_search; +using std::smatch; namespace lib { @@ -61,7 +63,8 @@ namespace test { virtual void run (Arg) { - simpeUsage(); +// simpeUsage(); + verify_parsing(); verify_instantiation(); verify_keySubstituton(); verify_conditional(); @@ -85,6 +88,161 @@ namespace test { } + /** @test TODO + * @note the regular expression \ref ACCEPT_FIELD is comprised of several + * alternatives and optional parts, which are marked by 5 sub-expressions + * - 1 ≙ end token + * - 2 ≙ some logic token ("if" or "for") + * - 3 ≙ a key or key path + * - 4 ≙ else token (which must be solitary) + * - 5 ≙ an escaped field (which should not be processed) + * @todo WIP 4/24 🔁 define ⟶ implement + */ + void + verify_parsing() + { + smatch mat; + string input; + CHECK (not regex_search (input, mat, ACCEPT_MARKUP)); + + input = " Hallelujah "; + CHECK (not regex_search (input, mat, ACCEPT_MARKUP)); + + input = " stale${beer}forever "; +SHOW_EXPR(input) + CHECK (regex_search (input, mat, ACCEPT_MARKUP)); +SHOW_EXPR(mat.position()) + CHECK (mat.position() == 6); +SHOW_EXPR(mat.length()) + CHECK (mat.length() == 7); +SHOW_EXPR(mat.prefix()) + CHECK (mat.prefix() == " stale"_expect); +SHOW_EXPR(mat.suffix()) + CHECK (mat.suffix() == "forever "_expect); +SHOW_EXPR(string(mat[0])) + CHECK (mat[0] == "${beer}"_expect); +SHOW_EXPR(string(mat[1])) + CHECK (not mat[1].matched); +SHOW_EXPR(string(mat[2])) + CHECK (not mat[2].matched); +SHOW_EXPR(string(mat[3])) + CHECK (mat[3] == "beer"_expect); +SHOW_EXPR(string(mat[4])) + CHECK (not mat[4].matched); +SHOW_EXPR(string(mat[5])) + CHECK (not mat[5].matched); + + input = " watch ${for stale}${beer} whatever "; +SHOW_EXPR(input) + CHECK (regex_search (input, mat, ACCEPT_MARKUP)); +SHOW_EXPR(mat.position()) + CHECK (mat.position() == 7); +SHOW_EXPR(mat.length()) + CHECK (mat.length() == 12); +SHOW_EXPR(mat.prefix()) + CHECK (mat.prefix() == " watch "_expect); +SHOW_EXPR(mat.suffix()) + CHECK (mat.suffix() == "${beer} whatever "_expect); +SHOW_EXPR(string(mat[0])) + CHECK (mat[0] == "${for stale}"_expect); +SHOW_EXPR(string(mat[1])) + CHECK (not mat[1].matched); +SHOW_EXPR(string(mat[2])) + CHECK (mat[2] == "for"_expect); +SHOW_EXPR(string(mat[3])) + CHECK (mat[3] == "stale"_expect); +SHOW_EXPR(string(mat[4])) +SHOW_EXPR(string(mat[5])) + + input = " work ${end if beer} however "; +SHOW_EXPR(input) + CHECK (regex_search (input, mat, ACCEPT_MARKUP)); +SHOW_EXPR(mat.position()) + CHECK (mat.position() == 6); +SHOW_EXPR(mat.length()) + CHECK (mat.length() == 15); +SHOW_EXPR(mat.prefix()) + CHECK (mat.prefix() == " work "_expect); +SHOW_EXPR(mat.suffix()) + CHECK (mat.suffix() == " however "_expect); +SHOW_EXPR(string(mat[0])) + CHECK (mat[0] == "${end if beer}"_expect); +SHOW_EXPR(string(mat[1])) + CHECK (mat[1] == "end "_expect); +SHOW_EXPR(string(mat[2])) + CHECK (mat[2] == "if"_expect); +SHOW_EXPR(string(mat[3])) + CHECK (mat[3] == "beer"_expect); +SHOW_EXPR(string(mat[4])) +SHOW_EXPR(string(mat[5])) + + input = " catch ${end while stale}${endfor brown.beer} ever "; +SHOW_EXPR(input) + CHECK (regex_search (input, mat, ACCEPT_MARKUP)); +SHOW_EXPR(mat.position()) + CHECK (mat.position() == 25); +SHOW_EXPR(mat.length()) + CHECK (mat.length() == 20); +SHOW_EXPR(mat.prefix()) + CHECK (mat.prefix() == " catch ${end while stale}"_expect); +SHOW_EXPR(mat.suffix()) + CHECK (mat.suffix() == " ever "_expect); +SHOW_EXPR(string(mat[0])) + CHECK (mat[0] == "${endfor brown.beer}"_expect); +SHOW_EXPR(string(mat[1])) + CHECK (mat[1] == "end"_expect); +SHOW_EXPR(string(mat[2])) + CHECK (mat[2] == "for"_expect); +SHOW_EXPR(string(mat[3])) + CHECK (mat[3] == "brown.beer"_expect); +SHOW_EXPR(string(mat[4])) +SHOW_EXPR(string(mat[5])) + + input = " catch ${else} ever "; +SHOW_EXPR(input) + CHECK (regex_search (input, mat, ACCEPT_MARKUP)); +SHOW_EXPR(mat.position()) + CHECK (mat.position() == 7); +SHOW_EXPR(mat.length()) + CHECK (mat.length() == 7); +SHOW_EXPR(mat.prefix()) + CHECK (mat.prefix() == " catch "_expect); +SHOW_EXPR(mat.suffix()) + CHECK (mat.suffix() == " ever "_expect); +SHOW_EXPR(string(mat[0])) + CHECK (mat[0] == "${else}"_expect); +SHOW_EXPR(string(mat[1])) +SHOW_EXPR(string(mat[2])) +SHOW_EXPR(string(mat[3])) + CHECK (mat[3] == "else"_expect); +SHOW_EXPR(string(mat[4])) +SHOW_EXPR(string(mat[5])) + + input = " catch ${else if} fever \\${can.beer} "; +SHOW_EXPR(input) + CHECK (regex_search (input, mat, ACCEPT_MARKUP)); +SHOW_EXPR(mat.position()) + CHECK (mat.position() == 24); +SHOW_EXPR(mat.length()) + CHECK (mat.length() == 2); +SHOW_EXPR(mat.prefix()) + CHECK (mat.prefix() == " catch ${else if} fever "_expect); +SHOW_EXPR(mat.suffix()) + CHECK (mat.suffix() == "{can.beer} "_expect); +SHOW_EXPR(string(mat[0])) + CHECK (mat[0] == "\\$"_expect); +SHOW_EXPR(string(mat[1])) +SHOW_EXPR(string(mat[2])) + CHECK (not mat[2].matched); +SHOW_EXPR(string(mat[3])) + CHECK (not mat[3].matched); +SHOW_EXPR(string(mat[4])) + CHECK (not mat[4].matched); +SHOW_EXPR(string(mat[5])) + CHECK (mat[5] == "\\$"_expect); + } + + /** @test TODO * @todo WIP 4/24 🔁 define ⟶ implement */ diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index c3fd19b75..e9bc0f713 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -112531,9 +112531,38 @@ std::cout << tmpl.render({"what", "World"}) << s - + + + + + + + + + + + + + + + +

+ als Einstellung des konkreten Templates? ⟹ das wäre einfach, aber unpraktisch für den Client +

+

+ als Einstellung im Data-Binding? ⟹ das wäre einfach für den Client, wäre aber für Standard-Templates endgütlig festgelegt +

+

+ über einen freien Erweiterungspunkt? ⟹ die optimal flexible Lösung, aber trickreich zu realisieren und schwer zuverlässig zu steuern +

+ + +
+
+
+ @@ -113000,14 +113029,60 @@ std::cout << tmpl.render({"what", "World"}) << s
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + +