2016-02-27 01:47:33 +01:00
|
|
|
/*
|
|
|
|
|
TEST-MUTATION-TARGET.hpp - diagnostic helper for TreeMutator bindings
|
|
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
2016, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU General Public License as
|
|
|
|
|
published by the Free Software Foundation; either version 2 of
|
|
|
|
|
the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @file test-mutation-target.hpp
|
|
|
|
|
** Diagnostic helper for unit tests regarding mutation of custom data.
|
|
|
|
|
** The TreeMutator provides a specialised adapter to attach to a TestMutationTarget.
|
|
|
|
|
** This adapter is optional and can be combined with any other binding for arbitrary
|
|
|
|
|
** hierarchical data structures. It operates in the way of a "wire tap", where the
|
|
|
|
|
** observed "mutation primitives" are recorded within the TestMutationTarget,
|
|
|
|
|
** which offers query functions for the unit test to verify what happened.
|
|
|
|
|
**
|
|
|
|
|
** @remarks this facility was created during the attempt to shape the internal API
|
|
|
|
|
** of TreeMutator, including definition of the "mutation primitives";
|
|
|
|
|
** it might be helpful later to diagnose problems with data mutation.
|
|
|
|
|
**
|
|
|
|
|
** @todo WIP 2/2016
|
|
|
|
|
**
|
2016-05-26 02:16:34 +02:00
|
|
|
** @see TreeMutatorBinding_test
|
2016-02-27 01:47:33 +01:00
|
|
|
** @see TreeMutator
|
|
|
|
|
**
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LIB_DIFF_TEST_MUTATION_TARGET_H
|
|
|
|
|
#define LIB_DIFF_TEST_MUTATION_TARGET_H
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/error.hpp"
|
|
|
|
|
#include "lib/symbol.hpp"
|
|
|
|
|
#include "lib/diff/record.hpp"
|
|
|
|
|
#include "lib/diff/tree-mutator.hpp"
|
|
|
|
|
#include "lib/idi/genfunc.hpp"
|
2016-03-11 21:30:25 +01:00
|
|
|
#include "lib/format-string.hpp"
|
2016-03-11 17:39:25 +01:00
|
|
|
#include "lib/format-util.hpp"
|
2016-02-27 01:47:33 +01:00
|
|
|
#include "lib/test/event-log.hpp"
|
2016-03-03 23:11:36 +01:00
|
|
|
#include "lib/util.hpp"
|
2016-02-27 01:47:33 +01:00
|
|
|
|
2016-03-03 23:11:36 +01:00
|
|
|
#include <boost/noncopyable.hpp>
|
2016-03-04 22:30:11 +01:00
|
|
|
#include <utility>
|
2016-02-27 01:47:33 +01:00
|
|
|
#include <string>
|
2016-03-03 23:52:21 +01:00
|
|
|
#include <vector>
|
2016-02-27 01:47:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
namespace diff{
|
|
|
|
|
|
|
|
|
|
namespace error = lumiera::error;
|
|
|
|
|
|
2016-03-11 21:30:25 +01:00
|
|
|
using lib::Literal;
|
2016-02-27 01:47:33 +01:00
|
|
|
using lib::test::EventLog;
|
|
|
|
|
using lib::test::EventMatch;
|
2016-03-11 17:39:25 +01:00
|
|
|
using iter_stl::eachElm;
|
2016-03-03 23:52:21 +01:00
|
|
|
using util::unConst;
|
|
|
|
|
using util::isnil;
|
2016-03-11 17:39:25 +01:00
|
|
|
using util::join;
|
2016-03-11 21:30:25 +01:00
|
|
|
using util::_Fmt;
|
|
|
|
|
|
2016-03-04 22:30:11 +01:00
|
|
|
using std::string;
|
|
|
|
|
using std::forward;
|
|
|
|
|
using std::move;
|
2016-02-27 01:47:33 +01:00
|
|
|
|
2016-03-04 22:30:11 +01:00
|
|
|
|
|
|
|
|
namespace { // diagnostic helpers: render diff spec....
|
|
|
|
|
|
2016-03-03 23:11:36 +01:00
|
|
|
template<typename T>
|
2016-03-03 23:52:21 +01:00
|
|
|
inline string
|
2016-03-03 23:11:36 +01:00
|
|
|
identify (const T* const entity)
|
|
|
|
|
{
|
|
|
|
|
return lib::idi::instanceTypeID(entity);
|
|
|
|
|
}
|
2016-03-03 23:52:21 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
string render (DataCap const&);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline string
|
|
|
|
|
renderAttribute (GenNode const& elm)
|
|
|
|
|
{
|
|
|
|
|
return elm.idi.getSym()
|
|
|
|
|
+ " = "
|
|
|
|
|
+ render (elm.data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline string
|
|
|
|
|
renderChild (GenNode const& elm)
|
|
|
|
|
{
|
|
|
|
|
return render (elm.data);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 22:30:11 +01:00
|
|
|
inline string
|
|
|
|
|
renderNode (GenNode const& n)
|
|
|
|
|
{
|
|
|
|
|
return n.isNamed()? renderAttribute(n)
|
|
|
|
|
: renderChild(n);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-03 23:52:21 +01:00
|
|
|
|
|
|
|
|
inline string
|
|
|
|
|
renderRecord (Rec const& record)
|
2016-03-04 22:30:11 +01:00
|
|
|
{
|
|
|
|
|
using util::join;
|
|
|
|
|
using lib::transformIterator;
|
|
|
|
|
|
|
|
|
|
return "Rec("
|
|
|
|
|
+ (Rec::TYPE_NIL==record.getType()? "" : record.getType())
|
|
|
|
|
+ (isnil(record.attribs())? "" : "| "+join (transformIterator (record.attribs(), renderAttribute))+" ")
|
|
|
|
|
+ (isnil(record.scope())? "" : "|{"+join (transformIterator (record.scope(), renderChild))+"}")
|
|
|
|
|
+ ")"
|
|
|
|
|
;
|
|
|
|
|
}
|
2016-03-03 23:52:21 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
inline string
|
|
|
|
|
render (DataCap const& content)
|
2016-03-04 22:30:11 +01:00
|
|
|
{
|
2016-08-08 14:20:54 +02:00
|
|
|
struct StringRenderer
|
|
|
|
|
: public Variant<DataValues>::Visitor
|
2016-03-04 22:30:11 +01:00
|
|
|
{
|
2016-08-08 14:20:54 +02:00
|
|
|
string representation{""};
|
2016-03-04 22:30:11 +01:00
|
|
|
|
2016-03-03 23:52:21 +01:00
|
|
|
#define STRINGIFY_CONTENT(_TY_) \
|
2016-08-08 14:20:54 +02:00
|
|
|
virtual void handle (_TY_& val) override { representation = util::toString (val); }
|
2016-03-04 22:30:11 +01:00
|
|
|
|
|
|
|
|
STRINGIFY_CONTENT (int)
|
|
|
|
|
STRINGIFY_CONTENT (int64_t)
|
|
|
|
|
STRINGIFY_CONTENT (short)
|
|
|
|
|
STRINGIFY_CONTENT (char)
|
|
|
|
|
STRINGIFY_CONTENT (bool)
|
|
|
|
|
STRINGIFY_CONTENT (double)
|
|
|
|
|
STRINGIFY_CONTENT (string)
|
|
|
|
|
STRINGIFY_CONTENT (time::Time)
|
|
|
|
|
STRINGIFY_CONTENT (time::Offset)
|
|
|
|
|
STRINGIFY_CONTENT (time::Duration)
|
|
|
|
|
STRINGIFY_CONTENT (time::TimeSpan)
|
|
|
|
|
STRINGIFY_CONTENT (hash::LuidH)
|
|
|
|
|
|
|
|
|
|
/** recursive simplified content-only rendering
|
|
|
|
|
* of an embedded Record<GenNode> */
|
|
|
|
|
virtual void
|
|
|
|
|
handle (Rec & rec) override
|
|
|
|
|
{
|
2016-08-08 14:20:54 +02:00
|
|
|
representation = renderRecord (rec);
|
2016-03-04 22:30:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
|
handle (RecRef & ref) override
|
|
|
|
|
{
|
|
|
|
|
if (ref)
|
2016-08-08 14:20:54 +02:00
|
|
|
representation = renderRecord (ref);
|
2016-03-04 22:30:11 +01:00
|
|
|
else
|
2016-08-08 14:20:54 +02:00
|
|
|
representation = util::BOTTOM_INDICATOR;
|
2016-03-04 22:30:11 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
StringRenderer visitor;
|
|
|
|
|
unConst(content).accept (visitor);
|
2016-08-08 14:20:54 +02:00
|
|
|
return visitor.representation;
|
2016-03-04 22:30:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}//(End)diagnostic helpers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test adapter to watch and verify how the
|
|
|
|
|
* TreeMutator binds to custom tree data structures.
|
2016-03-11 21:30:25 +01:00
|
|
|
* As a data structure, the TestMutationTarget builds an
|
|
|
|
|
* »External Tree Description« reflecting the actual data structure,
|
|
|
|
|
* as can be inferred through listening to all handled diff mutation primitives.
|
|
|
|
|
* Besides, each of these primitives is recorded in the embedded \ref EventLog.
|
2016-05-26 02:16:34 +02:00
|
|
|
* @see TreeMutatorBinding_test::mutateDummy()
|
2016-02-27 01:47:33 +01:00
|
|
|
*/
|
|
|
|
|
class TestMutationTarget
|
2016-03-03 23:11:36 +01:00
|
|
|
: boost::noncopyable
|
2016-02-27 01:47:33 +01:00
|
|
|
{
|
2016-03-11 17:39:25 +01:00
|
|
|
using VecG = std::vector<GenNode>;
|
|
|
|
|
|
2016-03-03 23:11:36 +01:00
|
|
|
EventLog log_{identify (this)};
|
|
|
|
|
|
2016-03-11 17:39:25 +01:00
|
|
|
VecG content_{};
|
|
|
|
|
VecG prev_content_{};
|
2016-02-27 01:47:33 +01:00
|
|
|
|
2016-03-04 22:30:11 +01:00
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
public:
|
2016-03-11 17:39:25 +01:00
|
|
|
using iterator = typename iter_stl::_SeqT<VecG>::Range;
|
|
|
|
|
using const_iterator = typename iter_stl::_SeqT<const VecG>::Range;
|
2016-03-04 22:30:11 +01:00
|
|
|
|
2016-03-11 17:39:25 +01:00
|
|
|
const_iterator begin() const { return eachElm(content_); }
|
2016-03-04 22:30:11 +01:00
|
|
|
const_iterator end() const { return const_iterator(); }
|
|
|
|
|
|
2016-03-11 17:39:25 +01:00
|
|
|
iterator srcIter() { return eachElm(prev_content_); }
|
|
|
|
|
iterator lastElm() { return iterator(VecG::iterator(&content_.back()), content_.end());}
|
2016-03-04 22:30:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
friend const_iterator begin (TestMutationTarget const& target) { return target.begin(); }
|
|
|
|
|
friend const_iterator end (TestMutationTarget const& target) { return target.end(); }
|
|
|
|
|
|
|
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
|
2016-03-03 23:11:36 +01:00
|
|
|
/* === Operation / Mutation API === */
|
|
|
|
|
|
2016-03-25 20:46:48 +01:00
|
|
|
iterator
|
2016-03-03 23:11:36 +01:00
|
|
|
initMutation (string mutatorID)
|
|
|
|
|
{
|
2016-03-06 03:55:31 +01:00
|
|
|
prev_content_.clear();
|
2016-03-03 23:52:21 +01:00
|
|
|
swap (content_, prev_content_);
|
2016-03-03 23:11:36 +01:00
|
|
|
log_.event ("attachMutator "+mutatorID);
|
2016-03-25 20:46:48 +01:00
|
|
|
return srcIter();
|
2016-03-03 23:11:36 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-03 23:52:21 +01:00
|
|
|
void
|
2016-03-11 17:39:25 +01:00
|
|
|
inject (GenNode&& elm, string operationID)
|
2016-03-03 23:52:21 +01:00
|
|
|
{
|
2016-03-11 17:39:25 +01:00
|
|
|
content_.emplace_back (forward<GenNode>(elm));
|
|
|
|
|
log_.event (operationID, renderNode (content_.back()));
|
2016-03-04 22:30:11 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-11 17:39:25 +01:00
|
|
|
static iterator
|
|
|
|
|
search (GenNode::ID const& targetID, iterator pos)
|
2016-03-04 22:30:11 +01:00
|
|
|
{
|
2016-03-11 17:39:25 +01:00
|
|
|
while (pos and not pos->matches(targetID))
|
|
|
|
|
++pos;
|
2016-03-04 22:30:11 +01:00
|
|
|
return pos;
|
2016-03-03 23:52:21 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-11 17:39:25 +01:00
|
|
|
iterator
|
|
|
|
|
locate (GenNode::ID const& targetID)
|
|
|
|
|
{
|
|
|
|
|
if (!empty() and content_.back().matches(targetID))
|
|
|
|
|
return lastElm();
|
|
|
|
|
else
|
|
|
|
|
return search (targetID, eachElm(content_));
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 23:18:25 +01:00
|
|
|
void
|
2016-03-11 17:39:25 +01:00
|
|
|
logSkip (GenNode const& content)
|
2016-03-04 23:18:25 +01:00
|
|
|
{
|
2016-03-11 17:39:25 +01:00
|
|
|
log_.event ("skipSrc", isnil(content.idi.getSym())? util::BOTTOM_INDICATOR : renderNode(content));
|
2016-03-04 23:18:25 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-11 21:30:25 +01:00
|
|
|
void
|
|
|
|
|
logAssignment (GenNode const& target, string oldPayload)
|
|
|
|
|
{
|
|
|
|
|
log_.event ("assignElm", _Fmt{"%s: %s ⤅ %s"}
|
|
|
|
|
% target.idi.getSym()
|
|
|
|
|
% oldPayload
|
|
|
|
|
% render(target.data));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
logMutation (GenNode const& target)
|
|
|
|
|
{
|
|
|
|
|
log_.event ("mutateChild", _Fmt{"%s: start mutation...%s"}
|
|
|
|
|
% target.idi.getSym()
|
|
|
|
|
% render(target.data));
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-24 22:23:06 +02:00
|
|
|
void
|
|
|
|
|
logScopeCompletion (iterator processingPos)
|
|
|
|
|
{
|
2016-06-09 01:21:06 +02:00
|
|
|
log_.event ("completeScope", _Fmt{"⤴ scope%s completed / %d waste elm(s)"}
|
2016-05-24 22:23:06 +02:00
|
|
|
% (processingPos? " NOT":"")
|
|
|
|
|
% prev_content_.size());
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-03 23:11:36 +01:00
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
/* === Diagnostic / Verification === */
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
empty() const
|
|
|
|
|
{
|
2016-03-03 23:11:36 +01:00
|
|
|
return content_.empty();
|
2016-02-27 01:47:33 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-11 17:39:25 +01:00
|
|
|
/** render payload content for diagnostics */
|
|
|
|
|
string
|
|
|
|
|
showContent () const
|
2016-02-27 01:47:33 +01:00
|
|
|
{
|
2016-03-11 17:39:25 +01:00
|
|
|
return join (transformIterator (begin(), renderNode));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** render elements waiting in source buffer to be accepted */
|
|
|
|
|
string
|
|
|
|
|
showSrcBuffer () const
|
|
|
|
|
{
|
|
|
|
|
return join (transformIterator (eachElm(prev_content_), renderNode));
|
2016-02-27 01:47:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventMatch
|
|
|
|
|
verify (string match) const
|
|
|
|
|
{
|
|
|
|
|
return getLog().verify(match);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventMatch
|
|
|
|
|
verifyMatch (string regExp) const
|
|
|
|
|
{
|
|
|
|
|
return getLog().verifyMatch(regExp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventMatch
|
|
|
|
|
verifyEvent (string match) const
|
|
|
|
|
{
|
|
|
|
|
return getLog().verifyEvent(match);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventMatch
|
|
|
|
|
verifyEvent (string classifier, string match) const
|
|
|
|
|
{
|
|
|
|
|
return getLog().verifyEvent (classifier,match);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventMatch
|
|
|
|
|
verifyCall (string match) const
|
|
|
|
|
{
|
|
|
|
|
return getLog().verifyCall(match);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventMatch
|
|
|
|
|
ensureNot (string match) const
|
|
|
|
|
{
|
|
|
|
|
return getLog().ensureNot(match);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventLog const&
|
|
|
|
|
getLog() const
|
|
|
|
|
{
|
|
|
|
|
return log_;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace { // supply a suitable decorator for the TreeMutator
|
|
|
|
|
|
|
|
|
|
template<class PAR>
|
2016-03-18 00:31:04 +01:00
|
|
|
class TestWireTap
|
2016-03-03 23:52:21 +01:00
|
|
|
: public PAR
|
2016-02-27 01:47:33 +01:00
|
|
|
{
|
2016-03-04 22:30:11 +01:00
|
|
|
using Target = TestMutationTarget;
|
|
|
|
|
using Iter = TestMutationTarget::iterator;
|
2016-03-18 00:31:04 +01:00
|
|
|
|
|
|
|
|
Target& target_;
|
|
|
|
|
Iter pos_;
|
|
|
|
|
|
|
|
|
|
public:
|
2016-03-26 02:01:31 +01:00
|
|
|
TestWireTap(Target& dummy, PAR&& chain)
|
|
|
|
|
: PAR{forward<PAR> (chain)}
|
2016-03-18 00:31:04 +01:00
|
|
|
, target_(dummy)
|
2016-10-03 23:54:09 +02:00
|
|
|
, pos_()
|
2016-03-25 20:46:48 +01:00
|
|
|
{ }
|
2016-03-18 00:31:04 +01:00
|
|
|
|
2016-03-04 22:30:11 +01:00
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
|
2016-03-03 23:11:36 +01:00
|
|
|
/* ==== re-Implementation of the operation API ==== */
|
2016-10-03 23:54:09 +02:00
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
|
init() override
|
|
|
|
|
{
|
|
|
|
|
pos_ = target_.initMutation (identify(this));
|
|
|
|
|
PAR::init();
|
|
|
|
|
}
|
2016-03-04 21:26:25 +01:00
|
|
|
|
2016-03-25 21:40:30 +01:00
|
|
|
/** record in the test target
|
2016-03-03 23:52:21 +01:00
|
|
|
* that a new child element is
|
2016-03-25 21:40:30 +01:00
|
|
|
* being inserted at current position
|
investigate and confirm the logic underlying the matchSrc, skipSrc and acceptSrc primitives
In Theory, acceptSrc and skipSrc are to operate symmetrically,
with the sole difference that skipSrc does not move anything
into the new content.
BUT, since skipSrc is also used to implement the `skip` verb,
which serves to discard garbage left back by a preceeding `find`,
we cannot touch the data found in the src position without risk
of SEGFAULT. For this reason, there is a dedicated matchSrc operation,
which shall be used to generate the verification step to properly
implement the `del` verb.
I've spent quite some time to verify the logic of predicate evaluation.
It seems to be OK: whenever the SELECTOR applies, then we'll perform
the local match, and then also we'll perform the skipSrc. Otherwise,
we'll delegate both operations likewise to the next lower layer,
without touching anything here.
2016-08-09 23:42:42 +02:00
|
|
|
* @remarks TestWireTap adapter together with TestMutationTarget
|
|
|
|
|
* maintain a "shadow copy" of the data and apply the detected diff
|
|
|
|
|
* against this internal copy. This allows to verify what's going on
|
2016-03-04 22:30:11 +01:00
|
|
|
*/
|
2016-05-28 01:17:45 +02:00
|
|
|
virtual bool
|
2016-03-03 23:11:36 +01:00
|
|
|
injectNew (GenNode const& n) override
|
|
|
|
|
{
|
2016-03-11 17:39:25 +01:00
|
|
|
target_.inject (GenNode{n}, "injectNew");
|
2016-05-28 01:17:45 +02:00
|
|
|
return PAR::injectNew (n);
|
2016-03-03 23:11:36 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-04 23:18:25 +01:00
|
|
|
virtual bool
|
2016-05-24 21:34:08 +02:00
|
|
|
hasSrc () override
|
2016-03-04 23:18:25 +01:00
|
|
|
{
|
2016-05-24 21:34:08 +02:00
|
|
|
return pos_
|
|
|
|
|
or PAR::hasSrc();
|
2016-03-04 23:18:25 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-04 21:38:39 +01:00
|
|
|
/** ensure the next recorded source element
|
|
|
|
|
* matches on a formal level with given spec */
|
2016-03-04 21:26:25 +01:00
|
|
|
virtual bool
|
2016-03-04 23:18:25 +01:00
|
|
|
matchSrc (GenNode const& n) override
|
2016-03-04 21:26:25 +01:00
|
|
|
{
|
2016-03-12 01:01:26 +01:00
|
|
|
return PAR::matchSrc(n)
|
2016-08-13 17:34:57 +02:00
|
|
|
or(pos_ and n.matches(*pos_));
|
investigate and confirm the logic underlying the matchSrc, skipSrc and acceptSrc primitives
In Theory, acceptSrc and skipSrc are to operate symmetrically,
with the sole difference that skipSrc does not move anything
into the new content.
BUT, since skipSrc is also used to implement the `skip` verb,
which serves to discard garbage left back by a preceeding `find`,
we cannot touch the data found in the src position without risk
of SEGFAULT. For this reason, there is a dedicated matchSrc operation,
which shall be used to generate the verification step to properly
implement the `del` verb.
I've spent quite some time to verify the logic of predicate evaluation.
It seems to be OK: whenever the SELECTOR applies, then we'll perform
the local match, and then also we'll perform the skipSrc. Otherwise,
we'll delegate both operations likewise to the next lower layer,
without touching anything here.
2016-08-09 23:42:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** skip next recorded src element without touching it */
|
|
|
|
|
virtual void
|
|
|
|
|
skipSrc (GenNode const& n) override
|
|
|
|
|
{
|
|
|
|
|
if (pos_)
|
|
|
|
|
{
|
|
|
|
|
GenNode const& skippedElm = *pos_;
|
|
|
|
|
++pos_;
|
|
|
|
|
target_.logSkip (skippedElm);
|
|
|
|
|
}
|
|
|
|
|
PAR::skipSrc(n);
|
2016-03-04 21:26:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** accept existing element, when matching the given spec */
|
|
|
|
|
virtual bool
|
2016-03-04 23:18:25 +01:00
|
|
|
acceptSrc (GenNode const& n) override
|
2016-03-04 21:26:25 +01:00
|
|
|
{
|
2016-08-07 01:58:26 +02:00
|
|
|
bool isSrcMatch = pos_ and n.matches(*pos_);
|
investigate and confirm the logic underlying the matchSrc, skipSrc and acceptSrc primitives
In Theory, acceptSrc and skipSrc are to operate symmetrically,
with the sole difference that skipSrc does not move anything
into the new content.
BUT, since skipSrc is also used to implement the `skip` verb,
which serves to discard garbage left back by a preceeding `find`,
we cannot touch the data found in the src position without risk
of SEGFAULT. For this reason, there is a dedicated matchSrc operation,
which shall be used to generate the verification step to properly
implement the `del` verb.
I've spent quite some time to verify the logic of predicate evaluation.
It seems to be OK: whenever the SELECTOR applies, then we'll perform
the local match, and then also we'll perform the skipSrc. Otherwise,
we'll delegate both operations likewise to the next lower layer,
without touching anything here.
2016-08-09 23:42:42 +02:00
|
|
|
if (isSrcMatch) //NOTE: important to invoke our own match here
|
2016-03-12 01:01:26 +01:00
|
|
|
{
|
|
|
|
|
target_.inject (move(*pos_), "acceptSrc");
|
|
|
|
|
++pos_;
|
|
|
|
|
}
|
|
|
|
|
return PAR::acceptSrc(n)
|
|
|
|
|
or isSrcMatch;
|
2016-03-04 21:26:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** locate designated element and accept it at current position */
|
|
|
|
|
virtual bool
|
2016-03-11 17:39:25 +01:00
|
|
|
findSrc (GenNode const& ref) override
|
2016-03-04 21:26:25 +01:00
|
|
|
{
|
2016-03-11 17:39:25 +01:00
|
|
|
Iter found = TestMutationTarget::search (ref.idi, pos_);
|
2016-03-12 01:01:26 +01:00
|
|
|
if (found)
|
2016-03-04 22:30:11 +01:00
|
|
|
{
|
|
|
|
|
target_.inject (move(*found), "findSrc");
|
|
|
|
|
}
|
2016-03-12 01:01:26 +01:00
|
|
|
return PAR::findSrc(ref)
|
|
|
|
|
or found;
|
2016-03-04 21:26:25 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-06 03:55:31 +01:00
|
|
|
/** repeatedly accept, until after the designated location */
|
|
|
|
|
virtual bool
|
|
|
|
|
accept_until (GenNode const& spec)
|
|
|
|
|
{
|
2016-03-12 01:01:26 +01:00
|
|
|
bool foundTarget = true;
|
|
|
|
|
|
2016-03-10 20:53:36 +01:00
|
|
|
if (spec.matches (Ref::END))
|
2016-03-12 01:01:26 +01:00
|
|
|
for ( ; pos_; ++pos_)
|
|
|
|
|
target_.inject (move(*pos_), "accept_until END");
|
2016-08-13 17:34:57 +02:00
|
|
|
else
|
|
|
|
|
if (spec.matches (Ref::ATTRIBS))
|
|
|
|
|
for ( ; pos_ and pos_->isNamed(); ++pos_)
|
|
|
|
|
target_.inject (move(*pos_), "accept_until after ATTRIBS");
|
2016-03-10 20:53:36 +01:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
string logMsg{"accept_until "+spec.idi.getSym()};
|
|
|
|
|
while (pos_ and not TestWireTap::matchSrc(spec))
|
|
|
|
|
{
|
|
|
|
|
target_.inject (move(*pos_), logMsg);
|
|
|
|
|
++pos_;
|
|
|
|
|
}
|
|
|
|
|
if (TestWireTap::matchSrc(spec))
|
|
|
|
|
{
|
|
|
|
|
target_.inject (move(*pos_), logMsg);
|
|
|
|
|
++pos_;
|
|
|
|
|
}
|
|
|
|
|
else
|
2016-03-12 01:01:26 +01:00
|
|
|
foundTarget = false;
|
2016-03-10 20:53:36 +01:00
|
|
|
}
|
2016-03-12 01:01:26 +01:00
|
|
|
return PAR::accept_until(spec)
|
|
|
|
|
or foundTarget;
|
2016-03-06 03:55:31 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-18 00:31:04 +01:00
|
|
|
/** locate element already accepted into the target sequence
|
2016-03-11 21:30:25 +01:00
|
|
|
* and assign the designated payload value to it. */
|
2016-03-06 02:26:42 +01:00
|
|
|
virtual bool
|
|
|
|
|
assignElm (GenNode const& spec)
|
|
|
|
|
{
|
2016-03-11 17:39:25 +01:00
|
|
|
Iter targetElm = target_.locate (spec.idi);
|
2016-03-12 01:01:26 +01:00
|
|
|
if (targetElm)
|
|
|
|
|
{
|
|
|
|
|
string logOldPayload{render(targetElm->data)};
|
|
|
|
|
*targetElm = spec;
|
|
|
|
|
target_.logAssignment (*targetElm, logOldPayload);
|
|
|
|
|
}
|
|
|
|
|
return PAR::assignElm(spec)
|
|
|
|
|
or targetElm;
|
2016-03-06 02:26:42 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-18 00:31:04 +01:00
|
|
|
/** locate the designated target element and build a suitable
|
2016-03-06 02:26:42 +01:00
|
|
|
* sub-mutator for this element into the provided target buffer */
|
|
|
|
|
virtual bool
|
2016-06-09 01:18:21 +02:00
|
|
|
mutateChild (GenNode const& spec, TreeMutator::Handle targetBuff)
|
2016-03-06 02:26:42 +01:00
|
|
|
{
|
2016-03-12 01:01:26 +01:00
|
|
|
if (PAR::mutateChild (spec, targetBuff))
|
|
|
|
|
return true;
|
|
|
|
|
else // Test mode only --
|
|
|
|
|
{ // no other layer was able to provide a mutator
|
|
|
|
|
Iter targetElm = target_.locate (spec.idi);
|
|
|
|
|
if (targetElm)
|
|
|
|
|
{
|
|
|
|
|
targetBuff.create (TreeMutator::build());
|
|
|
|
|
target_.logMutation (*targetElm);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2016-03-06 02:26:42 +01:00
|
|
|
}
|
|
|
|
|
|
2016-05-24 22:23:06 +02:00
|
|
|
/** verify all our pending (old) source elements where mentioned.
|
|
|
|
|
* @note allows chained "onion-layers" to clean-up and verify.*/
|
|
|
|
|
virtual bool
|
|
|
|
|
completeScope()
|
|
|
|
|
{
|
|
|
|
|
target_.logScopeCompletion (pos_);
|
|
|
|
|
return PAR::completeScope()
|
|
|
|
|
and isnil(this->pos_);
|
|
|
|
|
}
|
2016-02-27 01:47:33 +01:00
|
|
|
};
|
|
|
|
|
|
2016-03-03 23:52:21 +01:00
|
|
|
|
2016-02-27 01:47:33 +01:00
|
|
|
template<class PAR>
|
reorganise inclusion of TreeMutator-DSL builders
previously they where included in the middle of tree-mutator.hpp
This was straight forward, since the builder relies on the classes
defined in the detail headers.
However, the GenNode-binding needs to use a specifically configured
collection binding, and this in turn requires writing a recursive
lambda to deal with nested scopes. This gets us into trouble with
circular definition dependencies.
As a workaround we now only *declare* the DSL builder functions
in the tree-mutator-builder object, and additionally use auto on
all return types. This allows us to spell out the complete builder
definition, without mentioning any of the implementation classes.
Obviously, the detail headers have then to be included *after*
the builder definition, at bottom of tree-mutator.hpp
This also allows us to turn these implementation headers into
completely normal headers, with namespaces and transitive #includes
In the end, the whole setup looks much more "innocent" now.
But beware: the #include of the implementation headers at bottom
of tree-mutator.hpp needs to be given in reverse dependency order,
due to the circular inclusion (back to tree-mutator.hpp) in
conjunction with the inclusion guards!
2016-09-02 01:29:32 +02:00
|
|
|
auto
|
2016-02-27 01:47:33 +01:00
|
|
|
Builder<PAR>::attachDummy (TestMutationTarget& dummy)
|
|
|
|
|
{
|
reorganise inclusion of TreeMutator-DSL builders
previously they where included in the middle of tree-mutator.hpp
This was straight forward, since the builder relies on the classes
defined in the detail headers.
However, the GenNode-binding needs to use a specifically configured
collection binding, and this in turn requires writing a recursive
lambda to deal with nested scopes. This gets us into trouble with
circular definition dependencies.
As a workaround we now only *declare* the DSL builder functions
in the tree-mutator-builder object, and additionally use auto on
all return types. This allows us to spell out the complete builder
definition, without mentioning any of the implementation classes.
Obviously, the detail headers have then to be included *after*
the builder definition, at bottom of tree-mutator.hpp
This also allows us to turn these implementation headers into
completely normal headers, with namespaces and transitive #includes
In the end, the whole setup looks much more "innocent" now.
But beware: the #include of the implementation headers at bottom
of tree-mutator.hpp needs to be given in reverse dependency order,
due to the circular inclusion (back to tree-mutator.hpp) in
conjunction with the inclusion guards!
2016-09-02 01:29:32 +02:00
|
|
|
return chainedBuilder<TestWireTap<PAR>> (dummy);
|
2016-02-27 01:47:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::diff
|
|
|
|
|
#endif /*LIB_DIFF_TREE_MUTATOR_H*/
|