From bc8e947f3c21bb2a8f3039bbdefceffca96b5c65 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 24 Mar 2024 14:21:44 +0100 Subject: [PATCH] Library: remould compiler to active iteration ...turns out the ''pipeline design'' is not a good fit for the Action compilation, since the compiler needs to refer to previous Actions; better to let the compiler ''build'' the `ActionSeq` --- src/lib/text-template.hpp | 70 ++++++++++++---------------- tests/library/text-template-test.cpp | 9 ++-- wiki/thinkPad.ichthyo.mm | 40 ++++++++++------ 3 files changed, 61 insertions(+), 58 deletions(-) diff --git a/src/lib/text-template.hpp b/src/lib/text-template.hpp index 2bf3bdbc7..e22e10620 100644 --- a/src/lib/text-template.hpp +++ b/src/lib/text-template.hpp @@ -205,6 +205,8 @@ namespace lib { + + /*****************************************//** * Text template substitution engine */ @@ -230,6 +232,7 @@ namespace lib { Idx begin{0}; Idx after{0}; }; + using ScopeStack = std::stack>; struct Action { @@ -246,7 +249,6 @@ namespace lib { /** processor in a parse pipeline — yields sequence of Actions */ - template class ActionCompiler; /** Binding to a specific data source. @@ -312,56 +314,40 @@ namespace lib { * for conditionals and iteration, some cross-linking is necessary, based on index * numbers for the actions emitted and coordinated by a stack of bracketing constructs. */ - template class TextTemplate::ActionCompiler - : public PAR { Idx idx_{0}; - Action currToken_{TEXT, initLead()}; - optional post_{nullopt}; + ScopeStack scope_{}; public: - using PAR::PAR; - - /* === state core protocol === */ - - bool - checkPoint() const + template + ActionSeq + buildActions (PAR&& parseIter) { - return PAR::checkPoint() - or bool(post_); - } - - Action const& - yield() const - { - return currToken_; - } - - void - iterNext() - { - ++idx_; - if (post_) - post_ = nullopt; - else - currToken_ = compile(); + ActionSeq actions; + actions.emplace_back (Action{TEXT, initLead(parseIter)}); + while (parseIter) + { + idx_ = actions.size(); + actions.emplace_back( + compile (parseIter, actions.back().code)); + } + return actions; } private: + template Action - compile() + compile (PAR& parseIter, Code currCode) { //...throws if exhausted - TagSyntax& tag = PAR::yield(); - auto isState = [this](Code c){ return c == currToken_.code; }; - auto nextState = [this, &tag] { + TagSyntax& tag = *parseIter; + auto isState = [&](Code c){ return c == currCode; }; + auto nextState = [&] { StrView lead = tag.tail; - PAR::iterNext(); + ++parseIter; // first expose intermittent text before next tag - if (PAR::checkPoint()) - lead = PAR::yield().lead; - else // expose tail after final match - post_ = lead; + if (parseIter) + lead = parseIter->lead; return Action{TEXT, string{lead}}; }; switch (tag.syntax) { @@ -375,9 +361,12 @@ namespace lib { if (isState (COND)) return nextState(); ///////////////////////////////////////////////////OOO push IF-clause here + scope_.push (ParseCtx{IF, idx_}); return Action{COND, tag.key}; case TagSyntax::END_IF: ///////////////////////////////////////////////////OOO verify and pop IF-clause here +// if (scope_.empty() or +// (not isnil(tag.key) scope_.top()) return nextState(); case TagSyntax::FOR: if (isState (ITER)) @@ -407,10 +396,11 @@ namespace lib { } } + template string - initLead() ///< first Action must present the literal text before the first tag + initLead (PAR& parseIter) ///< first Action must present the literal text before the first tag { - return string{PAR::checkPoint()? PAR::yield().lead : ""}; + return string{parseIter? parseIter->lead : ""}; } }; diff --git a/tests/library/text-template-test.cpp b/tests/library/text-template-test.cpp index d76040e0d..9faca41e2 100644 --- a/tests/library/text-template-test.cpp +++ b/tests/library/text-template-test.cpp @@ -220,8 +220,8 @@ namespace test { // Generate sequence of Action tokens from parsing results auto render = [](TextTemplate::Action const& act) -> string { return _Fmt{"‖%d|↷%d‖▷%s"} % uint(act.code) % act.refIDX % act.val; }; -SHOW_EXPR(util::join(parse(input) - .processingLayer() + auto act1 = TextTemplate::ActionCompiler().buildActions(parse(input)); +SHOW_EXPR(util::join(explore(act1) .transform(render) , "▶")) input = R"~( @@ -234,9 +234,8 @@ SHOW_EXPR(util::join(parse(input) if nested}loop-suffix${else}${end for} tail... )~"; - auto compiler = parse(input) - .processingLayer(); -SHOW_EXPR(util::join(compiler.transform(render),"▶\n▶")) + auto actions = TextTemplate::ActionCompiler().buildActions(parse(input)); +SHOW_EXPR(util::join (explore(actions).transform(render),"▶\n▶")) } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index a0a04bdf9..09b58f1d4 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -112745,8 +112745,7 @@ std::cout << tmpl.render({"what", "World"}) << s ...dieser enthält die Informationen aus dem RegExp-Match bereits semantisch aufgeschlüsselt

- - + @@ -112756,8 +112755,7 @@ std::cout << tmpl.render({"what", "World"}) << s ...das heißt, die einzelne Auswertung ist keine pure function — aber der Seiteneffekt-Stat verbleibt in der Pipeline selber und merkt sich den Endpunkt des vorausgehenden Matches

- -
+
@@ -113122,8 +113120,7 @@ std::cout << tmpl.render({"what", "World"}) << s man könnte wohl was basteln mit den Funktionen position(i) und length(i)

- - +
@@ -113139,8 +113136,7 @@ std::cout << tmpl.render({"what", "World"}) << s ...und diese soll irgendwie auf eine Pipeline aufbauen. Das bedeutet, die Lösung sollte möglichst in der Verarbeitung selber zugänglich sein, und nicht über eine externe Zusatz-Information oder einen Seiteneffekt. Es wäre denkbar, auf das Ende des letzten Match aufzubauen — allerdings noch viel schöner wäre es, wenn der letzte Match den Quell-String komplett ausschöpft, so daß gar kein Rest übrig bleibt

- - +
@@ -113179,7 +113175,7 @@ std::cout << tmpl.render({"what", "World"}) << s - + @@ -113208,8 +113204,7 @@ std::cout << tmpl.render({"what", "World"}) << s erst in einem zweiten Schritt wird explizit eine spezifische Action für diese Syntax emittiert

- - +
@@ -113227,8 +113222,7 @@ std::cout << tmpl.render({"what", "World"}) << s vom letzten beobachteten Syntax-Match als TEXT-lead ausgegeben

- - +
@@ -113255,6 +113249,26 @@ std::cout << tmpl.render({"what", "World"}) << s
+ + + + + + + + + + + + + + + + + + + +