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:
Fischlurch 2015-10-30 03:11:33 +01:00
parent e5ffcf224f
commit c94bbcbb15
2 changed files with 178 additions and 40 deletions

View file

@ -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);
}
};

View file

@ -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&#246;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 &#252;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&#xfc;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&#xf6;sungsans&#xe4;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&#252;r den geschachtelten Kontext
</p>
<p>
und gibt ihm rekursiv den Diff hinein. Wenn dieser Aufruf zur&#252;ckkehrt
</p>
<p>
ist der gesammte Diff f&#252;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&#xe4;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&#xdf; das Token anschauen"/>
<node CREATED="1446159999939" ID="ID_1385561919" MODIFIED="1446160014569" TEXT="nicht kompatibel mit userem &quot;Interpreter&quot;-Modell"/>
<node CREATED="1446159858701" ID="ID_751122481" MODIFIED="1446160039239" TEXT="Applikator mu&#xdf; 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&#252;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 &quot;Interpreter&quot;-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&#252;ndet duch die generische Architektur.
</p>
<p>
Die Trennung von Diff-Iteration und dem Interpreter erm&#246;glicht verschiedene Sprach-Ebenen.
</p>
<p>
Allerdings werde ich f&#252;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>