From a73e5ffffee5c87d9dc8ce2ef189642c72c02017 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 2 Sep 2016 18:40:16 +0200 Subject: [PATCH] TreeMutator binding: change handling of AFTER(Ref::ATTRIBS) this is a subtle change in the semantics of the diff language, actually IMHO a change towards the better. It was prompted by the desire to integrate diff application onto GenNode-trees into the implementation framework based on TreeMutator, and do away with the dedicated implementation. Now it is a matter of the *selector* to decide if a given layer is responsible for "attributes". If so, then *all* elements within this layer count as "attribute" and an after(Ref::ATTRIBS) verb will fast forward behind *the end of this layer* Note that the meta token Ref::ATTRIBS is a named GenNode, and thus trivially responds to isNamed() == true --- .../diff/tree-mutator-attribute-binding.hpp | 7 +- .../diff/tree-mutator-collection-binding.hpp | 16 +-- src/lib/diff/tree-mutator.hpp | 20 +++- .../diff/tree-mutator-binding-test.cpp | 17 ++- wiki/renderengine.html | 13 ++- wiki/thinkPad.ichthyo.mm | 101 +++++++++++++++--- 6 files changed, 134 insertions(+), 40 deletions(-) diff --git a/src/lib/diff/tree-mutator-attribute-binding.hpp b/src/lib/diff/tree-mutator-attribute-binding.hpp index 588a7a10d..4185bb6ea 100644 --- a/src/lib/diff/tree-mutator-attribute-binding.hpp +++ b/src/lib/diff/tree-mutator-attribute-binding.hpp @@ -219,11 +219,8 @@ namespace diff{ virtual bool accept_until (GenNode const& spec) override { - if (Ref::END == spec) - return PAR::accept_until(Ref::END); - else - if (Ref::ATTRIBS == spec) - return true; + if (Ref::END == spec or Ref::ATTRIBS == spec) + return PAR::accept_until(spec); else { __ifApplicable_refuse_to ("navigate to a position behind", spec); diff --git a/src/lib/diff/tree-mutator-collection-binding.hpp b/src/lib/diff/tree-mutator-collection-binding.hpp index 6812d6508..b9f85ebc4 100644 --- a/src/lib/diff/tree-mutator-collection-binding.hpp +++ b/src/lib/diff/tree-mutator-collection-binding.hpp @@ -296,15 +296,15 @@ namespace diff{ virtual bool accept_until (GenNode const& spec) { - if (spec.matches (Ref::END)) + if (spec.matches (Ref::END) + or + (spec.matches (Ref::ATTRIBS) + and binding_.isApplicable (Ref::ATTRIBS))) { for ( ; pos_; ++pos_) binding_.inject (move(*pos_)); - return true; - } - else - if (spec.matches (Ref::ATTRIBS)) return PAR::accept_until (spec); + } else if (binding_.isApplicable(spec)) { @@ -471,9 +471,11 @@ namespace diff{ } static bool - ignore_selector (GenNode const&) + ignore_selector (GenNode const& spec) { - return true; // by default apply diff unconditionally + return spec != Ref::ATTRIBS; + // by default apply diff unconditionally, + // but don't respond to after(ATTRIBS) } static bool diff --git a/src/lib/diff/tree-mutator.hpp b/src/lib/diff/tree-mutator.hpp index 6884f8548..4d5ebad02 100644 --- a/src/lib/diff/tree-mutator.hpp +++ b/src/lib/diff/tree-mutator.hpp @@ -261,10 +261,11 @@ namespace diff{ /** repeatedly accept, until after the designated location */ virtual bool - accept_until (GenNode const&) + accept_until (GenNode const& spec) { - // do nothing by default - return false; + return (Ref::END == spec or Ref::ATTRIBS == spec); + // contents are exhausted by default, + // yet we're unable to find something specific } /** locate designated element and accept it at current position */ @@ -469,7 +470,9 @@ namespace diff{ * - the optional _selector closure_ (CollectionBindingBuilder::isApplicableIf) * allows to limit applicability of this whole binding (layer) to only some * diff specs. E.g., we may set up a binding for elements with value semantics - * and another binding layer on top to deal with object like children (sub scopes) + * and another binding layer on top to deal with object like children (sub scopes). + * Please note that this selector also gets to judge the Ref::ATTRIBS spec, which + * means this layer's contents can be considered "attributes". * - the optional _setter closure_ (CollectionBindingBuilder::assignElement) accepts * a diff spec (GenNode) and should assign an equivalent value to the internal * data representation of the corresponding element (typically by constructing @@ -481,6 +484,15 @@ namespace diff{ * representation of the nested scope to enter. The code invoking this closure * typically pushes the buffer on some internal stack and switches then to use * this nested mutator until encountering the corresponding `EMU` bracket verb. + * @note the `after(Ref::ATTRIBS)` verb can only processed if the selector responds + * correct to a Ref::ATTRIBS spec. The implicit default selector does so, i.e. + * it rejects `Ref::ATTRIBS`. Please be sure to accept this token _only_ if + * your layer indeed holds something meant to implement "attributes", because + * in that case, the verb `after(Ref::ATTRIBS)` will fast forward and accept + * all the current contents of this layer + * @warning please note the _nested DSL_. The builder functions used to define + * the various closures are to be invoked on the _argument_ ("`collection(xyz)`"), + * not on the top level builder... */ template auto attach (BIN&& collectionBindingSetup); diff --git a/tests/library/diff/tree-mutator-binding-test.cpp b/tests/library/diff/tree-mutator-binding-test.cpp index c136f65cf..63232e2e7 100644 --- a/tests/library/diff/tree-mutator-binding-test.cpp +++ b/tests/library/diff/tree-mutator-binding-test.cpp @@ -454,11 +454,12 @@ namespace test{ CHECK (mutator2.matchSrc (ATTRIB1)); // current head element of src "matches" the given spec CHECK (isnil (target)); // the match didn't change anything - CHECK (not mutator2.accept_until(Ref::ATTRIBS)); - // NOTE: collection values can be anything; thus this + CHECK (mutator2.accept_until(Ref::ATTRIBS)); + CHECK (mutator2.matchSrc (ATTRIB1)); // NOTE: collection values can be anything; thus this // collection binding layer can not have any notion of - // "this is an attribute". It will just delegate to the - // next lower layer and thus finally return false + // "this is an attribute". It will not accept anything + // and just delegate to the next lower layer, which here + // is the empty binding and thus finally returns true CHECK (mutator2.accept_until(ATTRIB3)); // ...but of course we can fast forward to dedicated values // accept_until CHECK (!isnil (target)); // the fast forward did indeed accept some entries @@ -801,11 +802,7 @@ namespace test{ // what /is/ allowed though, for reasons of logic, // is to "fast forward behind all attributes" // of course this is implemented as NOP - CHECK (not mutator2.accept_until(Ref::END)); // accept_until END - // likewise for Ref::END, - // but in this case the call is actually forwarded down - // and thus returns false in our setup here, - // since there is no active layer below + CHECK (mutator2.accept_until(Ref::END)); // likewise for Ref::END // accept_until END mutator2.injectNew (ATTRIB2); // injectNew @@ -871,7 +868,7 @@ namespace test{ CHECK (not mutator3.accept_until (ATTRIB2)); // unknown binding, no one is responsible CHECK (not mutator3.accept_until (ATTRIB1)); CHECK (mutator3.accept_until (Ref::ATTRIBS)); // only the generic end-of-scope marks supported - CHECK (not mutator3.accept_until (Ref::END)); // (and implemented either as NOP or passed down) + CHECK (mutator3.accept_until (Ref::END)); // (and implemented as NOP plus forwarding down) // explanation: due to the nature of a 'data field', // this binding has no notion of 'ordering' and thus no 'current position'. diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 7b02e40f8..638e54458 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -8228,7 +8228,7 @@ On receiving a stream of tokens of this "diff language", it is possibl i.e. a ''unified diff'' or the ''predicate notation'' used above to describe the list diffing algorithm, just by accumulating changes. -
+
The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
 within the [[diff framework|TreeDiffModel]], this is a crucial joint, since here the abstract, generic, ~DOM-like ExternalTreeDescription meeds opaque, local and undisclosed data structures.
 
@@ -8302,6 +8302,7 @@ the building blocks, from which such a diff application can be combined, are emb
 ;operation {{{accept_until}}}
 :repeatedly //accept// elements from the old state buffer, until hitting a condition. The condition is given within the //spec//
 :* when the spec is {{{Ref::END}}}, then accept all (remaining) elements, i.e. the remainder of the old sequence as-is.
+:* when the spec is {{{Ref::ATTRIBS}}}, and the implementation supports the notion of "an attribute", then accept and stop behind attributes
 :* otherwise, accept //until after// finding an element that matches the //spec//. That is, the matching element will be the last one accepted
 ;operation {{{findSrc}}}
 :without changing the current position, delve further into the old state sequence, to find an element //matching the given spec.//
@@ -8364,12 +8365,16 @@ attached to a clip, or the mixture of clips, effects and labels found within a [
 :* ''mutateAttrib'': "key", mutator-builder lambda {{{void(TreeMutator::MutatorBuffer)}}}
 :** fabricate a recursive sub-TreeMutator to handle an //object valued// attribute
 :** implicit typing -- the fabricated sub-TreeMutator just needs to be able to deal with the bound attribute
+;GenNode binding
+:in the end, it turnes out that the //implementation// underlying our ExternalTreeDescription itself fits nicely into the above mentioned standard bindings --
+:all we have to do is to layer two collection bindings on top of each other, with a suitable //selector// to separate //attributes// from //children.//
+:obviously, this unified implementation comes at the price of one additional indirection (namely through the ~TreeMutator VTable), while the requirements on temporary storage size are almost identical (two iterators and two back references instead of one). Moreover, all the explicitly coded complexities regarding the attribute / children distinction is gone, relying on the logic of layered decorators solely. So the effort to shape the TreeMutator API payed off in the end, to improve and rectify the whole design...
 
 
 You should note that this entire design is recursive: only after understanding the part, where, for handling a sub-structure, a nested mutator is fabricated and placed into a given buffer, it becomes clear to what effect we're creating a customised mutator: we always need some (relative) parent scope, which happens to know more about the actual data to be treated with a TreeMutator. This scope assists with creating a suitable binding. Obviously, from within that binding, it is known what the sub-structures and children of that local data are all about and what semantics to give to the fundamental operations.
 
-
+
The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
 
 !Motivation
@@ -8512,6 +8517,10 @@ To create a ''binding'' for any object field, we need
 * either a ''setter'' (lambda) or a ''mutator builder'' (lambda)
 We have a distinct typing for each individual attribute, but this typing remains implicit: the lambda has to know what value to extract from the GenNode payload within the accepted diff message and how to apply this value. Especially for ''time entities'', which are modelled as immutable values, the lambda might want to build a [[time mutator|TimeMutation]]. And especially in the UI, timing specifications are expected to be translated into a //presentation grid.//
 
+!!!types, attributes and layering
+During the analysis it turned out that support for a mixed set of //typed child elements// is an important goal. However, we may impose the restriction that these typed sub collections will be kept segregated and not be mixed up within the child scope. Effectively, this was already the fundamental trait for the //symbolic object representation// chosen here. Thus, "attributes" become just one further kind of typed children; even "metadata" might be treated along these lines in future (currently as of 2016 we have only a type field in {{{diff::Record}}}, which is treated explicitly).
+This observation leads directly to an implementation based on layered decorators. Each kind of target elements will be treated by another decorator exclusively, and control is passed through a chain of such decorators. This means, each decorator (or "onion-layer") gets into charge based on a //selector predicate,// which works on the given diff verb solely (no introspection and no "peeking" into the implementation data). We should note though, that this implies some fine points of the diff language semantics are subject to this architectural decision -- especially the precise behaviour of the {{{AFTER}}}-verb depends on the //concrete layer structure// used at the target of the diff. Yet this seems acceptable, since it is the very nature of diff application to be dependent on internals of the target location -- just by definition we assume this inner structure of the target to be semantically congruent with the diff's source structure.
+
 
 !Implementation
 __June 2016__: after defining and implementing several concrete bindings successfully, the TreeMutator interface can be considered a given now. It seems conceivable even to try re-implementing the (already existing, hard wired) binding for GenNode in terms of a TreeMutator with specially outfitted binding. Anyway, matters are investigated and clarified enough in order to build the actual diff application based on this new TreeMutator interface. Some tricky structural and technical problems remain to be solved for that
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index f8cd640e0..853a9d193 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -563,6 +563,10 @@
 
 
 
+
+
+
+
 
 
 
@@ -683,7 +687,13 @@
 
 
 
-
+
+
+
+
+
+
+
 
 
 
@@ -838,7 +848,9 @@
 
 
 
-
+
+
+
 
 
 
@@ -1081,6 +1093,7 @@
 
 
 
+
 
 
   
@@ -1416,7 +1429,7 @@
 
 
 
-
+
 
 
 
@@ -1870,10 +1883,10 @@
 
 
 
-
+
 
 
-
+
 
 
   
@@ -2057,7 +2070,7 @@
 
 
 
-
+
 
 
 
@@ -2308,7 +2321,7 @@
 
 
 
-
+
 
 
 
@@ -4244,8 +4257,7 @@
 
 
 
-
-
+
 
 
 
@@ -4264,7 +4276,7 @@
 
 
 
-
+
 
 
 
@@ -4273,11 +4285,32 @@
 
 
 
-
+
+
+
+  
+    
+  
+  
+    

+ zwei Bindings +

+ + +
+ + +
+ + + + + + @@ -4298,8 +4331,52 @@ - + + + + + + + + + + + + + + + + + + + + + +

+ Mut -> Rekursion +

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