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`
This commit is contained in:
Fischlurch 2024-03-24 14:21:44 +01:00
parent b835d6a012
commit bc8e947f3c
3 changed files with 61 additions and 58 deletions

View file

@ -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<ParseCtx, std::vector<ParseCtx>>;
struct Action
{
@ -246,7 +249,6 @@ namespace lib {
/** processor in a parse pipeline — yields sequence of Actions */
template<class PAR>
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 PAR>
class TextTemplate::ActionCompiler
: public PAR
{
Idx idx_{0};
Action currToken_{TEXT, initLead()};
optional<StrView> post_{nullopt};
ScopeStack scope_{};
public:
using PAR::PAR;
/* === state core protocol === */
bool
checkPoint() const
template<class PAR>
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<class PAR>
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<class PAR>
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 : ""};
}
};

View file

@ -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<TextTemplate::ActionCompiler>()
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<TextTemplate::ActionCompiler>();
SHOW_EXPR(util::join(compiler.transform(render),"\n"))
auto actions = TextTemplate::ActionCompiler().buildActions(parse(input));
SHOW_EXPR(util::join (explore(actions).transform(render),"\n"))
}

View file

@ -112745,8 +112745,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
...dieser enth&#228;lt die Informationen aus dem RegExp-Match bereits semantisch aufgeschl&#252;sselt
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1711212113875" ID="ID_842279818" MODIFIED="1711212212420" TEXT="zudem wird eine Anschlu&#xdf;position im Functor &#xbb;versteckt mitgeschleppt&#xab;">
<richcontent TYPE="NOTE"><html>
@ -112756,8 +112755,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
...das hei&#223;t, die einzelne Auswertung ist keine <i>pure function </i>&#8212; aber der Seiteneffekt-Stat verbleibt in der Pipeline selber und merkt sich den Endpunkt des vorausgehenden Matches
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711211835240" ID="ID_1594550860" LINK="#ID_1703324697" MODIFIED="1711211884546" TEXT="Layer-2 : Behandeln und Ausfalten dieser F&#xe4;lle">
@ -113122,8 +113120,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
man k&#246;nnte wohl was basteln mit den Funktionen <font color="#8b1212" face="Monospaced">position(i)</font>&#160;und <font color="#8b1212" face="Monospaced">length(i)</font>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node CREATED="1711158230860" ID="ID_421709614" MODIFIED="1711158244517" TEXT="also dann halt gleich als String speichern"/>
@ -113139,8 +113136,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
...und diese soll <i>irgendwie</i>&#160;auf eine Pipeline aufbauen. Das bedeutet, die L&#246;sung sollte m&#246;glichst <i>in der Verarbeitung selber </i>zug&#228;nglich sein, und nicht &#252;ber eine externe Zusatz-Information oder einen Seiteneffekt. Es w&#228;re denkbar, auf das Ende des letzten Match aufzubauen &#8212; allerdings noch viel sch&#246;ner w&#228;re es, wenn der letzte Match den Quell-String <i>komplett aussch&#246;pft, </i>so da&#223; gar kein Rest &#252;brig bleibt
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node COLOR="#5b280f" CREATED="1711158504002" ID="ID_1524679089" MODIFIED="1711211519363" TEXT="Idee: pseudo-Match ganz auf das Ende der Quelle">
<icon BUILTIN="idea"/>
@ -113179,7 +113175,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
<icon BUILTIN="idea"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711212348956" ID="ID_363295345" MODIFIED="1711212359403" TEXT="custom-processing-Layer schaffen">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711212348956" ID="ID_363295345" MODIFIED="1711283010871" TEXT="pull-processing-Layer schaffen">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1711212365801" ID="ID_537344075" MODIFIED="1711212378412" TEXT="dieser enth&#xe4;lt die eigentliche compile-Logik"/>
<node CREATED="1711212381344" ID="ID_1526201544" MODIFIED="1711212412343" TEXT="&#x27f9; wird eine nested template in der eigentlichen Engine"/>
@ -113208,8 +113204,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
erst in einem zweiten Schritt wird explizit eine spezifische Action f&#252;r <i>diese Syntax </i>emittiert
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node COLOR="#338800" CREATED="1711218803245" ID="ID_843446113" MODIFIED="1711218950661" TEXT="Spezialfall &#xbb;Ende&#xab;">
@ -113227,8 +113222,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
vom letzten beobachteten Syntax-Match als TEXT-lead ausgegeben
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1711218915070" ID="ID_201326654" MODIFIED="1711218937703" TEXT="iterNext() erkennt das &#x27f9; schaltet den optional-tail wieder weg"/>
<node CREATED="1711218938586" ID="ID_253133952" MODIFIED="1711218948260" TEXT="danach: Trap-door">
@ -113255,6 +113249,26 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
</node>
</node>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1711245155902" ID="ID_1602057321" MODIFIED="1711245176070" TEXT="Problem: ID-Querverlinkung in der Pipeline">
<icon BUILTIN="messagebox_warning"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711245177524" ID="ID_1786845265" MODIFIED="1711282922187" TEXT="problematisch, da der Ziel-Container noch gar nicht existiert">
<icon BUILTIN="broken-line"/>
</node>
<node COLOR="#5b280f" CREATED="1711282883084" ID="ID_1047796766" MODIFIED="1711283013993" TEXT="&#x27f9; das bedeutet: f&#xfc;r den &#xbb;Compiler&#xab; ist das Pipeline-Design unpassend">
<icon BUILTIN="button_cancel"/>
<node CREATED="1711283145848" ID="ID_493051003" MODIFIED="1711283157218" TEXT="ehrlich gesagt: es war schon ganz sch&#xf6;n komplex"/>
<node CREATED="1711283158094" ID="ID_316804248" MODIFIED="1711283210930" TEXT="wiewohl im Rahmen des Iterator-Schemas (f&#xfc;r mich) verst&#xe4;ndlich"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711283024446" ID="ID_559056931" MODIFIED="1711283047068" TEXT="stattdessen: den Compiler als Builder-Komponente anlegen">
<icon BUILTIN="yes"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711283086632" ID="ID_396780439" MODIFIED="1711283215456" TEXT="erzeugen als freie (kopierbare) Komponente">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711283101287" ID="ID_1709357131" MODIFIED="1711283215456" TEXT="Arbeitsmethode: buildActions(ParseIterator)">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
<node CREATED="1711158288320" ID="ID_309368656" MODIFIED="1711158292632" TEXT="Test....">
<node CREATED="1711158297133" ID="ID_788373690" MODIFIED="1711158307246" TEXT="Iteration als Solche ist m&#xf6;glich"/>
<node COLOR="#435e98" CREATED="1711158308121" ID="ID_1938624994" MODIFIED="1711237020411" TEXT="K&#xfc;rzen des `lead` mu&#xdf; das Pattern selber &#xfc;berspringen">