reorganise the DSL aspect of the design

we want a simple and straight forward way of defining tokens
of the "diff language". Each token is bound to a specific
handler function in the language interpreter interface.
This commit is contained in:
Fischlurch 2014-12-14 03:47:23 +01:00
parent c911456909
commit e00a08b056
2 changed files with 89 additions and 45 deletions

View file

@ -47,7 +47,7 @@ namespace test{
LUMIERA_ERROR_DEFINE(DIFF_CONFLICT, "Collision in diff application: contents of target not as expected.");
template<typename E, class I>
template< class I, typename E>
struct DiffLanguage
{
@ -77,6 +77,58 @@ namespace test{
};
};
template<class I, typename E>
using HandlerFun = void (I::*) (E);
template<typename SIG_HANDLER>
struct DiffStepBuilder;
/** generator to produce specific language tokens */
template<class I, typename E>
struct DiffStepBuilder<HandlerFun<I,E>>
{
using Lang = DiffLanguage<I,E>;
using Step = typename Lang::DiffStep;
using Verb = typename Lang::DiffVerb;
HandlerFun<I,E> handler;
Literal id;
Step
operator() (E elm) const
{
return { Verb(handler,id), elm };
}
};
/** set up a diff language token generator,
* based on the specific handler function given.
* This generator will produce tokens, wrapping concrete content elements
* of type \c E. In the end, the purpose is to send a sequence of such tokens
* around, to feed them to a consumer, which implements the \em Interpreter
* interface of the diff language. E.g. this consumer might apply the diff.
*/
template<class H>
inline DiffStepBuilder<H>
diffTokenBuilder (H handlerFun, Literal id)
{
return { handlerFun, id };
}
/** shortcut to define tokens of the diff language.
* Use it to define namespace level function objects, which,
* when supplied with an argument value of type \c E, will generate
* a specific language token wrapping a copy of this element.
* @note need a typedef \c Interpreter at usage site
* to refer to the actual language interpreter interface;
* the template parameters of the Language and the element
* type will be picked up from the given member function pointer.
*/
#define DiffStep_CTOR(_ID_) \
const auto _ID_ = diffTokenBuilder (&Interpreter::_ID_, STRINGIFY(_ID_));
template<typename E>
class ListDiffInterpreter
@ -91,23 +143,11 @@ namespace test{
};
template<typename E>
struct ListDiffLanguage
: DiffLanguage<E, ListDiffInterpreter<E>>
{
using DiffStep = typename DiffLanguage<E, ListDiffInterpreter<E>>::DiffStep;
using DiffVerb = typename DiffLanguage<E, ListDiffInterpreter<E>>::DiffVerb;
using Ip = ListDiffInterpreter<E>;
#define DiffStep_CTOR(_ID_) \
static DiffStep _ID_(E e) { return {DiffVerb(&Ip::_ID_, STRINGIFY(_ID_)), e }; }
DiffStep_CTOR(ins);
DiffStep_CTOR(del);
DiffStep_CTOR(pick);
DiffStep_CTOR(push);
};
using ListDiffLanguage = DiffLanguage<ListDiffInterpreter<E>, E>;
template<class CON>
class DiffApplicationStrategy;
@ -281,7 +321,7 @@ namespace test{
return OnceIter(begin(ili), end(ili));
}
namespace {
namespace {//Test fixture....
using DataSeq = vector<string>;
@ -290,29 +330,33 @@ namespace test{
string TOK(a1), TOK(a2), TOK(a3), TOK(a4), TOK(a5);
string TOK(b1), TOK(b2), TOK(b3), TOK(b4);
struct TestDiff
: ListDiffLanguage<string>
{
using DiffSeq = iter_stl::IterSnapshot<DiffStep>;
static DiffSeq
generate()
{
return snapshot({del(a1)
, del(a2)
, ins(b1)
, pick(a3)
, push(a5)
, pick(a5)
, ins(b2)
, ins(b3)
, pick(a4)
, ins(b4)
});
}
};
}
using Interpreter = ListDiffInterpreter<string>;
using DiffStep = ListDiffLanguage<string>::DiffStep;
using DiffSeq = iter_stl::IterSnapshot<DiffStep>;
DiffStep_CTOR(ins);
DiffStep_CTOR(del);
DiffStep_CTOR(pick);
DiffStep_CTOR(push);
inline DiffSeq
generateTestDiff()
{
return snapshot({del(a1)
, del(a2)
, ins(b1)
, pick(a3)
, push(a5)
, pick(a5)
, ins(b2)
, ins(b3)
, pick(a4)
, ins(b4)
});
}
}//(End)Test fixture
@ -338,7 +382,7 @@ namespace test{
run (Arg)
{
DataSeq src({a1,a2,a3,a4,a5});
auto diff = TestDiff::generate();
auto diff = generateTestDiff();
CHECK (!isnil (diff));
DataSeq target = src;

View file

@ -7721,7 +7721,7 @@ The most fundamental distinction is the difference between //finding// a diff an
Before we can consider a diffing technique, we need to clarify the primitive operations used as foundation. These primitives form //a language.// This incurs a problem and choice of context. We might base the representation language on the situation where the diff is applied, we may care for the conciseness of the representation, or the effort and space required to apply it. We should consider if the focus is on reading and understanding the diff, if we intend to derive something directly from the diff representation -- say, if and what and when something is changed -- and we should consider how the context where the diff is established relates to the context where it is applied.
</pre>
</div>
<div title="TreeDiffModel" creator="Ichthyostega" modifier="Ichthyostega" created="201410270313" modified="201412120309" tags="Model GuiPattern spec draft" changecount="45">
<div title="TreeDiffModel" creator="Ichthyostega" modifier="Ichthyostega" created="201410270313" modified="201412120312" tags="Model GuiPattern spec draft" changecount="46">
<pre>for the purpose of handling updates in the GUI timeline display efficiently, we need to determine and represent //structural differences//
We build a slightly abstracted representation of tree changes and use this to propagate //change notifications// to the actual widgets. To keep the whole process space efficient, a demand-driven, stateless implementation approach is chosen. This reduces the problem into several layered stages.
* our model is a heterogeneous tree &amp;rArr; use demand-driven recursion
@ -7761,7 +7761,7 @@ Thus, for our specific usage scenario, the foremost relevant question is //how t
|{{{pick}}}(a~~4~~) |!| (b~~1~~, a~~3~~, a~~5~~, b~~2~~, b~~3~~, a~~4~~)|() |
|{{{ins}}}(b~~4~~) |!| (b~~1~~, a~~3~~, a~~5~~, b~~2~~, b~~3~~, a~~4~~, b~~4~~)|() |
__Implementation note__:The representation chosen here uses terms of constant size for the individual diff steps; in most cases, the argument is redundant and can be used for verification when applying the diff -- exceptions being the {{{push}}} and {{{ins}}} terms, where it actually encodes additional information. Especially the {{{puah}}}-representation is a compromise, since we encode as &quot;push the next term back behind the term a~~5~~&quot;. The more obvious rendering -- &quot;push term a~~4~~ back by +1 steps&quot; -- requires an additional integer argument not neccesary for any of the other diff verbs, defeating a fixed size value implementation.
__Implementation note__:The representation chosen here uses terms of constant size for the individual diff steps; in most cases, the argument is redundant and can be used for verification when applying the diff -- exceptions being the {{{push}}} and {{{ins}}} terms, where it actually encodes additional information. Especially the {{{push}}}-representation is a compromise, since we encode as &quot;push the next term back behind the term a~~5~~&quot;. The more obvious rendering -- &quot;push term a~~4~~ back by +1 steps&quot; -- requires an additional integer argument not neccesary for any of the other diff verbs, defeating a fixed size value implementation.
!!!extension to tree changes
Basically we could send messages for recursive descent right after each {{{pick}}} token -- yet, while minimal, such a representation would be unreadable, and requires a dedicated stack storage on both sides. Thus we arrange for the //recursive treatment of children// to be sent //postfix,// after the messages for the current node. Recursive descent is indicated by explicit (and slightly redundant) //bracketing tokens://