From c94bbcbb15a7f67f54a54549654f17a15024dc18 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 30 Oct 2015 03:11:33 +0100 Subject: [PATCH] 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. --- src/lib/diff/tree-diff-application.hpp | 79 ++++++++------ wiki/thinkPad.ichthyo.mm | 139 +++++++++++++++++++++++-- 2 files changed, 178 insertions(+), 40 deletions(-) diff --git a/src/lib/diff/tree-diff-application.hpp b/src/lib/diff/tree-diff-application.hpp index 0abf507c1..326fc1396 100644 --- a/src/lib/diff/tree-diff-application.hpp +++ b/src/lib/diff/tree-diff-application.hpp @@ -63,6 +63,7 @@ #include "lib/format-string.hpp" #include +#include 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 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 : 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 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); } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 120d9f64c..1e4cae219 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -1806,8 +1806,7 @@ - - + @@ -1820,8 +1819,7 @@ Lösung: wir arbeiten auf einem Mutator

- - + @@ -1839,8 +1837,7 @@ auf dem Umweg über einen ContentMutator

- - +
@@ -1861,8 +1858,7 @@ Innereien des alten Record verbrauchen

- - + @@ -1886,6 +1882,133 @@ + + + + + + +

+ Problem: Rekursion +

+ + +
+ + + + + + + + + + + + + +

+ wenn ein MUT kommt +

+

+ erzeugt man lokal einen DiffApplikator für den geschachtelten Kontext +

+

+ und gibt ihm rekursiv den Diff hinein. Wenn dieser Aufruf zurückkehrt +

+

+ ist der gesammte Diff für den eingeschachtelten Kontext konsumiert +

+ + +
+ +
+ + + + + + + + + + + + + + + + + + + +

+ wenn ein MUT kommt, +

+

+ pusht der Applikator seinen privaten Zustand +

+

+ auf einen explizit im Heap verwalteten std::stack +

+

+ und legt einen neuen Mutator an für den nested scope +

+ + +
+ +
+ + + + + + + + + + + + + + + + + +

+ Entscheidung: +

+

+ interner Stack +

+ + +
+ + + + + +

+ ....begründet duch die generische Architektur. +

+

+ Die Trennung von Diff-Iteration und dem Interpreter ermöglicht verschiedene Sprach-Ebenen. +

+

+ Allerdings werde ich für die Anwendung auf konkrete Datenstrukturen, +

+

+ also den TreeMutator, vermutlich das andere Modell (rekursiv konsumieren) verwenden. +

+ + +
+ +
+