extend storage arrangement to deal with nested child objects
It is difficult to reconcile our general architecture for the linearised diff representation with the processing of recursive, tree-like data structures. The natural and most clean way to deal with trees is to use recursion, i.e. the processor stack. But in our case, this means we'd have to peek into the next token of the language and then forward the diff iterator into a recursive call on the nested scope. Essentially, this breaks the separation between receiving a token sequence and interpretation for a concrete target data structure. For this reason, it is preferrable to make the stack an internal state of the concrete interpreter. The downside of this approach is the quite confusing data storage management; we try to make the role of the storage elements a bit more clear through descriptive accessor functions.
This commit is contained in:
parent
e5ffcf224f
commit
c94bbcbb15
2 changed files with 178 additions and 40 deletions
|
|
@ -63,6 +63,7 @@
|
|||
#include "lib/format-string.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <stack>
|
||||
|
||||
namespace lib {
|
||||
namespace diff{
|
||||
|
|
@ -73,7 +74,8 @@ namespace diff{
|
|||
|
||||
|
||||
/**
|
||||
* concrete strategy to apply a structural diff to a target data structure
|
||||
* Interpreter for the tree-diff-language to work on GenNode elements
|
||||
* A concrete strategy to apply a structural diff to a target data structure
|
||||
* made from #Record<GenNode> elements. This data structure is assumed to be
|
||||
* recursive, tree-like. But because Record elements are conceived as immutable
|
||||
* and value-like, the tree diff application actually works on a Rec::Mutator
|
||||
|
|
@ -86,38 +88,52 @@ namespace diff{
|
|||
class DiffApplicationStrategy<Rec::Mutator>
|
||||
: public TreeDiffInterpreter
|
||||
{
|
||||
using Mutator = Rec::Mutator;
|
||||
using Content = Rec::ContentMutator;
|
||||
using Iter = Content::Iter;
|
||||
|
||||
Rec::Mutator& target_;
|
||||
Content content_;
|
||||
|
||||
|
||||
|
||||
bool
|
||||
end_of_target()
|
||||
struct ScopeFrame
|
||||
{
|
||||
return content_.pos == content_.end();
|
||||
}
|
||||
Mutator& target;
|
||||
Content content;
|
||||
|
||||
ScopeFrame(Mutator& toModify)
|
||||
: target(toModify)
|
||||
, content()
|
||||
{
|
||||
target.swapContent (content);
|
||||
}
|
||||
};
|
||||
|
||||
/** Storage: a stack of workspaces
|
||||
* used to handle nested child objects */
|
||||
std::stack<ScopeFrame> scopes_;
|
||||
|
||||
|
||||
Mutator& out() { return scopes_.top().target; }
|
||||
Content& src() { return scopes_.top().content; }
|
||||
Iter& srcPos() { return scopes_.top().content.pos; }
|
||||
bool endOfData() { return srcPos() == src().end(); }
|
||||
|
||||
|
||||
void
|
||||
__expect_in_target (GenNode const& elm, Literal oper)
|
||||
{
|
||||
if (end_of_target())
|
||||
if (endOfData())
|
||||
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
|
||||
"no (further) elements in target sequence") % oper % elm
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
if (*content_.pos != elm)
|
||||
if (*srcPos() != elm)
|
||||
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
|
||||
"found element %s on current target position instead")
|
||||
% oper % elm % *content_.pos
|
||||
% oper % elm % *srcPos()
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
}
|
||||
|
||||
void
|
||||
__expect_further_elements (GenNode const& elm)
|
||||
{
|
||||
if (end_of_target())
|
||||
if (endOfData())
|
||||
throw error::State(_Fmt("Premature end of target sequence, still expecting element %s; "
|
||||
"unable to apply diff further.") % elm
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
|
|
@ -126,7 +142,7 @@ namespace diff{
|
|||
void
|
||||
__expect_found (GenNode const& elm, Iter const& targetPos)
|
||||
{
|
||||
if (targetPos == content_.end())
|
||||
if (targetPos == src().end())
|
||||
throw error::State(_Fmt("Premature end of sequence; unable to locate "
|
||||
"element %s in the remainder of the target.") % elm
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
|
|
@ -135,18 +151,18 @@ namespace diff{
|
|||
Iter
|
||||
find_in_current_scope (GenNode const& elm)
|
||||
{
|
||||
Iter end_of_scope = content_.currIsAttrib()? content_.attribs.end()
|
||||
: content_.children.end();
|
||||
return std::find (content_.pos, end_of_scope, elm);
|
||||
Iter end_of_scope = src().currIsAttrib()? src().attribs.end()
|
||||
: src().children.end();
|
||||
return std::find (srcPos(), end_of_scope, elm);
|
||||
}
|
||||
|
||||
void
|
||||
move_into_new_sequence (Iter pos)
|
||||
{
|
||||
if (content_.currIsAttrib())
|
||||
target_.appendAttrib (move(*pos));
|
||||
if (src().currIsAttrib())
|
||||
out().appendAttrib (move(*pos));
|
||||
else
|
||||
target_.appendChild (move(*pos));
|
||||
out().appendChild (move(*pos));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -157,12 +173,12 @@ namespace diff{
|
|||
ins (GenNode const& n) override
|
||||
{
|
||||
if (n.isNamed())
|
||||
target_.appendAttrib(n);
|
||||
out().appendAttrib(n);
|
||||
else
|
||||
{
|
||||
target_.appendChild(n);
|
||||
if (content_.currIsAttrib())
|
||||
content_.jumpToChildScope();
|
||||
out().appendChild(n);
|
||||
if (src().currIsAttrib())
|
||||
src().jumpToChildScope();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,22 +186,22 @@ namespace diff{
|
|||
del (GenNode const& n) override
|
||||
{
|
||||
__expect_in_target(n, "remove");
|
||||
++content_;
|
||||
++src();
|
||||
}
|
||||
|
||||
void
|
||||
pick (GenNode const& n) override
|
||||
{
|
||||
__expect_in_target(n, "pick");
|
||||
move_into_new_sequence (content_.pos);
|
||||
++content_;
|
||||
move_into_new_sequence (srcPos());
|
||||
++src();
|
||||
}
|
||||
|
||||
void
|
||||
skip (GenNode const& n) override
|
||||
{
|
||||
__expect_further_elements (n);
|
||||
++content_;
|
||||
++src();
|
||||
} // assume the actual content has been moved away by a previous find()
|
||||
|
||||
void
|
||||
|
|
@ -223,10 +239,9 @@ namespace diff{
|
|||
public:
|
||||
explicit
|
||||
DiffApplicationStrategy(Rec::Mutator& mutableTargetRecord)
|
||||
: target_(mutableTargetRecord)
|
||||
, content_()
|
||||
: scopes_()
|
||||
{
|
||||
target_.swapContent (content_);
|
||||
scopes_.emplace(mutableTargetRecord);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1806,8 +1806,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1445555721981" ID="ID_292803227" MODIFIED="1445556598964">
|
||||
|
|
@ -1820,8 +1819,7 @@
|
|||
Lösung: wir arbeiten <i>auf </i>einem Mutator
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1445556525714" ID="ID_1223708278" MODIFIED="1445556595037" TEXT="damit funktioniert es 1:1 wie List-Diff">
|
||||
<icon BUILTIN="ksmiletris"/>
|
||||
|
|
@ -1839,8 +1837,7 @@
|
|||
auf dem Umweg über einen <b>ContentMutator</b>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -1861,8 +1858,7 @@
|
|||
Innereien des alten Record <i>verbrauchen</i>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1445392105075" ID="ID_1377567733" MODIFIED="1445392113677" TEXT="zwei neue, lokale Vektoren aufbauen"/>
|
||||
<node CREATED="1445392114217" ID="ID_71124283" MODIFIED="1445392120973" TEXT="daraus den neuen Record konstruieren"/>
|
||||
|
|
@ -1886,6 +1882,133 @@
|
|||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1446159438278" HGAP="29" ID="ID_563496669" MODIFIED="1446159498995" VSHIFT="8">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Problem: <b><font color="#ed1c02" size="4">Rekursion</font></b>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
<node CREATED="1446159517914" ID="ID_1749096609" MODIFIED="1446159530661" TEXT="wir müssen rekursiv in Sub-Scope einsteigen"/>
|
||||
<node CREATED="1446159531745" ID="ID_1660140345" MODIFIED="1446159550795" TEXT="eingeschachtelt wieder ein Record::Mutator"/>
|
||||
<node CREATED="1446159551718" ID="ID_608514705" MODIFIED="1446159563593" TEXT="Wohin mit der Storage?"/>
|
||||
<node CREATED="1446159564725" ID="ID_893302390" MODIFIED="1446160764531" TEXT="zwei Lösungsansätze">
|
||||
<icon BUILTIN="info"/>
|
||||
<node CREATED="1446159584034" ID="ID_1845322273" MODIFIED="1446159588733" TEXT="rekursiv konsumieren">
|
||||
<node CREATED="1446159632603" ID="ID_1950074103" MODIFIED="1446160677236">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
wenn ein MUT kommt
|
||||
</p>
|
||||
<p>
|
||||
erzeugt man lokal einen DiffApplikator für den geschachtelten Kontext
|
||||
</p>
|
||||
<p>
|
||||
und gibt ihm rekursiv den Diff hinein. Wenn dieser Aufruf zurückkehrt
|
||||
</p>
|
||||
<p>
|
||||
ist der gesammte Diff für den eingeschachtelten Kontext konsumiert
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1446159744053" ID="ID_1735127502" MODIFIED="1446159765190" TEXT="pro">
|
||||
<node CREATED="1446159775161" ID="ID_192461503" MODIFIED="1446159832244" TEXT="elegant, selbstähnlich"/>
|
||||
<node CREATED="1446159790702" ID="ID_905335387" MODIFIED="1446159823622" TEXT="sauber, state bleibt lokal"/>
|
||||
<node CREATED="1446159833753" ID="ID_539121048" MODIFIED="1446159854442" TEXT="kein expliziter Stack, Daten auf dem Callstack"/>
|
||||
</node>
|
||||
<node CREATED="1446159751068" ID="ID_1466026714" MODIFIED="1446159770069" TEXT="contra">
|
||||
<node CREATED="1446159936323" ID="ID_1344861000" MODIFIED="1446159961793" TEXT="keine einfache Iteration mehr"/>
|
||||
<node CREATED="1446159910374" ID="ID_1307975840" MODIFIED="1446159998967" TEXT="man muß das Token anschauen"/>
|
||||
<node CREATED="1446159999939" ID="ID_1385561919" MODIFIED="1446160014569" TEXT="nicht kompatibel mit userem "Interpreter"-Modell"/>
|
||||
<node CREATED="1446159858701" ID="ID_751122481" MODIFIED="1446160039239" TEXT="Applikator muß selber Diff konsumieren"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1446159589369" ID="ID_1854873736" MODIFIED="1446159592429" TEXT="internen Stack">
|
||||
<node CREATED="1446160049196" ID="ID_893515494" MODIFIED="1446160680983">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
wenn ein MUT kommt,
|
||||
</p>
|
||||
<p>
|
||||
pusht der Applikator seinen privaten Zustand
|
||||
</p>
|
||||
<p>
|
||||
auf einen explizit im Heap verwalteten std::stack
|
||||
</p>
|
||||
<p>
|
||||
und legt einen neuen Mutator an für den nested scope
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1446160209758" ID="ID_1949126696" MODIFIED="1446160211514" TEXT="pro">
|
||||
<node CREATED="1446160223212" ID="ID_189646771" MODIFIED="1446160248517" TEXT="kompatibel mit unserem "Interpreter"-Modell"/>
|
||||
<node CREATED="1446160249609" ID="ID_1437127411" MODIFIED="1446160256555" TEXT="Diff wird einfach weiter iteriert"/>
|
||||
</node>
|
||||
<node CREATED="1446160212502" ID="ID_572617599" MODIFIED="1446160214449" TEXT="contra">
|
||||
<node CREATED="1446160522212" ID="ID_1824677628" MODIFIED="1446160588767" TEXT="wir manipulieren state in einer Komponente"/>
|
||||
<node CREATED="1446160621127" ID="ID_391572254" MODIFIED="1446160635561" TEXT="diese Manipulation passiert mitten aus einem Token-Aufruf"/>
|
||||
<node CREATED="1446160592347" ID="ID_126615043" MODIFIED="1446160608364" TEXT="Storage-Management ist verwirrend"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1446168257072" HGAP="81" ID="ID_91568209" MODIFIED="1446168481874" VSHIFT="5">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Entscheidung:
|
||||
</p>
|
||||
<p>
|
||||
interner Stack
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
....begründet duch die generische Architektur.
|
||||
</p>
|
||||
<p>
|
||||
Die Trennung von Diff-Iteration und dem Interpreter ermöglicht verschiedene Sprach-Ebenen.
|
||||
</p>
|
||||
<p>
|
||||
Allerdings werde ich für die Anwendung auf konkrete Datenstrukturen,
|
||||
</p>
|
||||
<p>
|
||||
also den TreeMutator, vermutlich das andere Modell (rekursiv konsumieren) verwenden.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue