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:
parent
b835d6a012
commit
bc8e947f3c
3 changed files with 61 additions and 58 deletions
|
|
@ -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 : ""};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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▶"))
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -112745,8 +112745,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
...dieser enthält die Informationen aus dem RegExp-Match bereits semantisch aufgeschlüsselt
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1711212113875" ID="ID_842279818" MODIFIED="1711212212420" TEXT="zudem wird eine Anschlußposition im Functor »versteckt mitgeschleppt«">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
|
|
@ -112756,8 +112755,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
...das heißt, die einzelne Auswertung ist keine <i>pure function </i>— 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älle">
|
||||
|
|
@ -113122,8 +113120,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
man könnte wohl was basteln mit den Funktionen <font color="#8b1212" face="Monospaced">position(i)</font> 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 << tmpl.render({"what", "World"}) << s
|
|||
...und diese soll <i>irgendwie</i> auf eine Pipeline aufbauen. Das bedeutet, die Lösung sollte möglichst <i>in der Verarbeitung selber </i>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 <i>komplett ausschöpft, </i>so daß gar kein Rest ü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 << tmpl.render({"what", "World"}) << 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ält die eigentliche compile-Logik"/>
|
||||
<node CREATED="1711212381344" ID="ID_1526201544" MODIFIED="1711212412343" TEXT="⟹ wird eine nested template in der eigentlichen Engine"/>
|
||||
|
|
@ -113208,8 +113204,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
erst in einem zweiten Schritt wird explizit eine spezifische Action fü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 »Ende«">
|
||||
|
|
@ -113227,8 +113222,7 @@ std::cout << tmpl.render({"what", "World"}) << 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 ⟹ schaltet den optional-tail wieder weg"/>
|
||||
<node CREATED="1711218938586" ID="ID_253133952" MODIFIED="1711218948260" TEXT="danach: Trap-door">
|
||||
|
|
@ -113255,6 +113249,26 @@ std::cout << tmpl.render({"what", "World"}) << 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="⟹ das bedeutet: für den »Compiler« 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ön komplex"/>
|
||||
<node CREATED="1711283158094" ID="ID_316804248" MODIFIED="1711283210930" TEXT="wiewohl im Rahmen des Iterator-Schemas (für mich) verstä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öglich"/>
|
||||
<node COLOR="#435e98" CREATED="1711158308121" ID="ID_1938624994" MODIFIED="1711237020411" TEXT="Kürzen des `lead` muß das Pattern selber überspringen">
|
||||
|
|
|
|||
Loading…
Reference in a new issue