Library: complete ETD data-source binding and test (closes #1359)
A minimalist `TextTemplate` engine is available for in-project use.
* supports only the bare minimum of features (no programming language)
* substitution of `${placeholder}` by key-name data access
* conditional section `${if key}...${end if}`
* iteration over a data sequence
* other then most solutions available as library,
this implementation does **not require** a specific data type,
nor does it invent a dynamic object system or JSON backend;
rather, a generic ''Data Source Adapter'' is used, which can
be specialised to access any kind of ''structured data''
* the following `DataSource` specialisations are provided
* `std::map<string,string>`
* Lumiera »External Tree Description« (based on `GenNode`)
* a string-based spec for testing
This commit is contained in:
parent
cfe54a5070
commit
918f96bb6f
5 changed files with 536 additions and 452 deletions
|
|
@ -49,8 +49,7 @@
|
|||
** can expand a `${value}` placeholder in that scope.
|
||||
** - Attributes of enclosing scopes are also visible — unless shadowed.
|
||||
** [ETD]: https://lumiera.org/documentation/design/architecture/ETD.html
|
||||
** @todo WIP-WIP-WIP 3/2024
|
||||
** @see TextTemplate_test
|
||||
** @see TextTemplate_test::verify_ETD_binding
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -60,124 +59,99 @@
|
|||
|
||||
#include "lib/diff/gen-node.hpp"
|
||||
#include "lib/text-template.hpp"
|
||||
//#include "lib/format-string.hpp"
|
||||
//#include "lib/format-util.hpp"
|
||||
//#include "lib/regex.hpp"
|
||||
//#include "lib/util.hpp"
|
||||
|
||||
//#include <memory>
|
||||
#include <string>
|
||||
//#include <vector>
|
||||
//#include <stack>
|
||||
//#include <map>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
using std::string;
|
||||
// using StrView = std::string_view;
|
||||
//
|
||||
// using util::_Fmt;
|
||||
// using util::isnil;
|
||||
// using util::unConst;
|
||||
|
||||
|
||||
namespace {
|
||||
namespace text_template {
|
||||
|
||||
}
|
||||
|
||||
/* ======= Data binding for GenNode (ETD) ======= */
|
||||
|
||||
/**
|
||||
* Data-binding for a tree of GenNode data (ETD).
|
||||
* Attributes are accessible as keys, while iteration descends
|
||||
* into the child scope of the attribute indicated in the ${for <key>}` tag.
|
||||
* @see TextTemplate_test::verify_ETD_binding()
|
||||
*/
|
||||
template<>
|
||||
struct TextTemplate::DataSource<diff::GenNode>
|
||||
{
|
||||
using Node = diff::GenNode;
|
||||
using Rec = diff::Rec;
|
||||
|
||||
Node const* data_;
|
||||
DataSource* parScope_;
|
||||
bool isSubScope() { return bool(parScope_); }
|
||||
|
||||
DataSource(Node const& root)
|
||||
: data_{&root}
|
||||
, parScope_{nullptr}
|
||||
{ }
|
||||
|
||||
|
||||
using Value = std::string;
|
||||
using Iter = Rec::scopeIter;
|
||||
|
||||
Node const*
|
||||
findNode (string key)
|
||||
{
|
||||
if (data_->isNested())
|
||||
{// standard case: Attribute lookup
|
||||
Rec const& record = data_->data.get<Rec>();
|
||||
if (record.hasAttribute (key))
|
||||
return & record.get (key);
|
||||
}
|
||||
else
|
||||
if ("value" == key) // special treatment for a »pseudo context«
|
||||
return data_; // comprised only of a single value node
|
||||
else
|
||||
if (isSubScope())
|
||||
return parScope_->findNode (key);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
contains (string key)
|
||||
{
|
||||
return bool(findNode (key));
|
||||
}
|
||||
|
||||
Value
|
||||
retrieveContent (string key)
|
||||
{
|
||||
return Value(findNode(key)->data); ///////////////////////////////OOO this is not correct -- need a way to render the bare content
|
||||
}
|
||||
|
||||
Iter
|
||||
getSequence (string key)
|
||||
{
|
||||
if (not contains(key))
|
||||
return Iter{};
|
||||
Node const* node = findNode (key);
|
||||
if (not node->isNested())
|
||||
return Iter{};
|
||||
else
|
||||
return node->data.get<Rec>().scope();
|
||||
}
|
||||
|
||||
DataSource
|
||||
openContext (Iter& iter)
|
||||
{
|
||||
REQUIRE (iter);
|
||||
DataSource nested{*this};
|
||||
nested.parScope_ = this;
|
||||
nested.data_ = & *iter;
|
||||
return nested;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace {// help the compiler with picking the proper specialisation for the data binding
|
||||
/* ======= Data binding for GenNode (ETD) ======= */
|
||||
|
||||
inline auto
|
||||
bindDataSource(diff::GenNode const& etd)
|
||||
/**
|
||||
* Data-binding for a tree of GenNode data (ETD).
|
||||
* Attributes are accessible as keys, while iteration descends
|
||||
* into the child scope of the attribute indicated in the ${for <key>}` tag.
|
||||
* @see TextTemplate_test::verify_ETD_binding()
|
||||
*/
|
||||
template<>
|
||||
struct DataSource<diff::GenNode>
|
||||
{
|
||||
return TextTemplate::DataSource<diff::GenNode>{etd};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
using Node = diff::GenNode;
|
||||
using Rec = diff::Rec;
|
||||
|
||||
Node const* data_;
|
||||
DataSource* parScope_;
|
||||
bool isSubScope() { return bool(parScope_); }
|
||||
|
||||
DataSource(Node const& root)
|
||||
: data_{&root}
|
||||
, parScope_{nullptr}
|
||||
{ }
|
||||
|
||||
|
||||
using Value = std::string;
|
||||
using Iter = Rec::scopeIter;
|
||||
|
||||
Node const*
|
||||
findNode (string key)
|
||||
{
|
||||
if (data_->isNested())
|
||||
{// standard case: Attribute lookup
|
||||
Rec const& record = data_->data.get<Rec>();
|
||||
if (record.hasAttribute (key))
|
||||
return & record.get (key);
|
||||
}
|
||||
else
|
||||
if ("value" == key) // special treatment for a »pseudo context«
|
||||
return data_; // comprised only of a single value node
|
||||
// ask parent scope...
|
||||
if (isSubScope())
|
||||
return parScope_->findNode (key);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
contains (string key)
|
||||
{
|
||||
return bool(findNode (key));
|
||||
}
|
||||
|
||||
Value
|
||||
retrieveContent (string key)
|
||||
{
|
||||
return renderCompact (*findNode(key));
|
||||
}
|
||||
|
||||
Iter
|
||||
getSequence (string key)
|
||||
{
|
||||
if (not contains(key))
|
||||
return Iter{};
|
||||
Node const* node = findNode (key);
|
||||
if (not node->isNested())
|
||||
return Iter{};
|
||||
else
|
||||
return node->data.get<Rec>().scope();
|
||||
}
|
||||
|
||||
DataSource
|
||||
openContext (Iter& iter)
|
||||
{
|
||||
REQUIRE (iter);
|
||||
DataSource nested{*this};
|
||||
nested.parScope_ = this;
|
||||
nested.data_ = & *iter;
|
||||
return nested;
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace text_template
|
||||
|
||||
}// namespace lib
|
||||
#endif /*LIB_TEXT_TEMPLATE_GEN_NODE_BINDING_H*/
|
||||
|
|
|
|||
|
|
@ -147,9 +147,10 @@
|
|||
**
|
||||
** \par ETD Binding
|
||||
** While the _Map Binding_ detailed in the preceding paragraph is mostly intended to handle
|
||||
** simple key substitutions, the more elaborate binding to `GenNode` data (ETD) is meant to
|
||||
** handle structural data, as encountered in the internal communication of components within
|
||||
** the Lumiera application — notably the »diff binding« used to populate the GUI with entities
|
||||
** simple key substitutions, the more elaborate binding to `GenNode` data (ETD), which is
|
||||
** provided in the separate header text-template-gen-node-binding.hpp, is meant to handle
|
||||
** structural data, as encountered in the internal communication of components within the
|
||||
** Lumiera application — notably the »diff binding« used to populate the GUI with entities
|
||||
** represented in the _Session Model_ in Steam-Layer. The mapping is straight-forward, as the
|
||||
** required concepts can be supported directly
|
||||
** - Key lookup is translated into _Attribute Lookup_ — starting in the current record and
|
||||
|
|
@ -158,10 +159,9 @@
|
|||
** for the iteration; thus each entity is again a `Rec<GenNode>` and can be represented
|
||||
** recursively as a DataSource<Rec<GenNode>>
|
||||
** - the DataSource implementation includes an _optional parent link,_ which is consulted
|
||||
** whenever _Attribute Lookup_ in the current record does not yield a result.
|
||||
** whenever _Attribute Lookup_ in the current record does not yield a result.
|
||||
** [External Tree Description]: https://lumiera.org/documentation/design/architecture/ETD.html
|
||||
** [Lumiera Forward Iterator]: https://lumiera.org/documentation/technical/library/iterator.html
|
||||
** @todo WIP-WIP-WIP 3/2024
|
||||
** @see TextTemplate_test
|
||||
** @see text-template-gen-node-binding.hpp
|
||||
** @see gnuplot-gen.hpp
|
||||
|
|
@ -192,6 +192,10 @@
|
|||
namespace lib {
|
||||
namespace error = lumiera::error;
|
||||
|
||||
namespace test { // declared friend for test access
|
||||
class TextTemplate_test;
|
||||
}
|
||||
|
||||
using std::string;
|
||||
using StrView = std::string_view;
|
||||
|
||||
|
|
@ -200,7 +204,7 @@ namespace lib {
|
|||
using util::unConst;
|
||||
|
||||
|
||||
namespace {
|
||||
namespace text_template { ///< Parser and DataSource binding for lib::TextTemplate
|
||||
|
||||
//-----------Syntax-for-iteration-control-in-map------
|
||||
const string MATCH_DATA_TOKEN = R"~(([^,;"\s]*)\s*)~";
|
||||
|
|
@ -307,17 +311,30 @@ namespace lib {
|
|||
return explore (util::RegexSearchIter{input, ACCEPT_MARKUP})
|
||||
.transform (classify);
|
||||
}
|
||||
}
|
||||
namespace test { // declared friend for test access
|
||||
class TextTemplate_test;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binding to a specific data source.
|
||||
* @note requires partial specialisation
|
||||
*/
|
||||
template<class DAT, typename SEL=void>
|
||||
class DataSource;
|
||||
|
||||
}//(namespace) text_template
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************//**
|
||||
* Text template substitution engine
|
||||
|
||||
/*************************************************//**
|
||||
* Text template substitution engine.
|
||||
* Can substitute `${placeholders}` by name and
|
||||
* can handle conditional and iterated sections.
|
||||
* Structural data for the substitution is accessed
|
||||
* and navigated through a generic _Data Source binding._
|
||||
* By default, a binding for Map-of-strings is provided.
|
||||
* @see TextTemplate_test
|
||||
*/
|
||||
class TextTemplate
|
||||
: util::MoveOnly
|
||||
|
|
@ -356,15 +373,10 @@ namespace lib {
|
|||
/** the text template is compiled into a sequence of Actions */
|
||||
using ActionSeq = std::vector<Action>;
|
||||
|
||||
|
||||
/** processor in a parse pipeline — yields sequence of Actions */
|
||||
class ActionCompiler;
|
||||
|
||||
/** Binding to a specific data source.
|
||||
* @note requires partial specialisation */
|
||||
template<class DAT, typename SEL=void>
|
||||
class DataSource;
|
||||
|
||||
/** Iterator »State Core« to process the template instantiation */
|
||||
template<class SRC>
|
||||
class InstanceCore
|
||||
{
|
||||
|
|
@ -428,8 +440,8 @@ namespace lib {
|
|||
/* ======= Parser / Compiler pipeline ======= */
|
||||
|
||||
/**
|
||||
* @remarks this is a »custom processing layer«
|
||||
* to be used in an [Iter-Explorer](\ref iter-explorer.hpp)-pipeline.
|
||||
* @remarks this builder component is used on top of a
|
||||
* [Iter-Explorer](\ref iter-explorer.hpp)-pipeline, based on a reg-exp.
|
||||
* The source layer (which is assumed to comply to the »State Core« concept),
|
||||
* yields TagSyntax records, one for each match of the ACCEPT_MARKUP reg-exp.
|
||||
* The actual compilation step, which is implemented as pull-processing here,
|
||||
|
|
@ -464,15 +476,15 @@ namespace lib {
|
|||
auto scopeClause = [&]{ return scope_.empty()? "??" : clause(scope_.top().clause); };
|
||||
|
||||
// Support for bracketing constructs (if / for)
|
||||
auto beginIdx = [&]{ return scope_.empty()? 0 : scope_.top().begin; }; // Index of action where scope was opened
|
||||
auto scopeKey = [&]{ return valid(beginIdx())? actions[beginIdx()].val : "";}; // Key controlling the if-/for-Scope
|
||||
auto keyMatch = [&]{ return isnil(parseIter->key) or parseIter->key == scopeKey(); }; // Key matches in opening and closing tag
|
||||
auto clauseMatch = [&](Clause c){ return not scope_.empty() and scope_.top().clause == c; }; // Kind of closing tag matches innermost scope
|
||||
auto beginIdx = [&]{ return scope_.empty()? 0 : scope_.top().begin; }; // Index of action where scope was opened
|
||||
auto scopeKey = [&]{ return valid(beginIdx())? actions[beginIdx()].val : "";}; // Key controlling the if-/for-Scope
|
||||
auto keyMatch = [&]{ return isnil(parseIter->key) or parseIter->key == scopeKey(); }; // Key matches in opening and closing tag
|
||||
auto clauseMatch = [&](Clause c){ return not scope_.empty() and scope_.top().clause == c; }; // Kind of closing tag matches innermost scope
|
||||
auto scopeMatch = [&](Clause c){ return clauseMatch(c) and keyMatch(); };
|
||||
|
||||
auto lead = [&]{ return parseIter->lead; };
|
||||
auto clashLead = [&]{ return actions[scope_.top().after - 1].val; }; // (for diagnostics: lead before a conflicting other "else")
|
||||
auto abbrev = [&](auto s){ return s.length()<16? s : s.substr(s.length()-15); }; // (shorten lead display to 15 chars)
|
||||
auto clashLead = [&]{ return actions[scope_.top().after - 1].val; }; // (for diagnostics: lead before a conflicting other "else")
|
||||
auto abbrev = [&](auto s){ return s.length()<16? s : s.substr(s.length()-15); }; // (shorten lead display to 15 chars)
|
||||
|
||||
// Syntax / consistency checks...
|
||||
auto __requireKey = [&](string descr)
|
||||
|
|
@ -480,8 +492,7 @@ namespace lib {
|
|||
if (isnil (parseIter->key))
|
||||
throw error::Invalid{_Fmt{"Tag without key: ...%s${%s |↯|}"}
|
||||
% abbrev(lead()) % descr
|
||||
};
|
||||
};
|
||||
}; };
|
||||
auto __checkBalanced = [&](Clause c)
|
||||
{
|
||||
if (not scopeMatch(c))
|
||||
|
|
@ -490,8 +501,7 @@ namespace lib {
|
|||
% scopeClause() % scopeKey()
|
||||
% abbrev(lead())
|
||||
% clause(c) % parseIter->key
|
||||
};
|
||||
};
|
||||
}; };
|
||||
auto __checkInScope = [&] {
|
||||
if (scope_.empty())
|
||||
throw error::Invalid{_Fmt{"Misplaced ...%s|↯|${else}"}
|
||||
|
|
@ -510,18 +520,19 @@ namespace lib {
|
|||
|
||||
// Primitives used for code generation....
|
||||
auto add = [&](Code c, string v){ actions.push_back (Action{c,v});};
|
||||
auto addCode = [&](Code c) { add ( c, parseIter->key); }; // add code token and transfer key picked up by parser
|
||||
auto addLead = [&] { add (TEXT, string{parseIter->lead}); }; // add TEXT token to represent the static part before this tag
|
||||
auto openScope = [&](Clause c){ scope_.push (ParseCtx{c, currIDX()}); }; // start nested scope for bracketing construct (if / for)
|
||||
auto closeScope = [&] { scope_.pop(); }; // close innermost nested scope
|
||||
auto addCode = [&](Code c) { add ( c, parseIter->key); }; // add code token and transfer key picked up by parser
|
||||
auto addLead = [&] { add (TEXT, string{parseIter->lead}); }; // add TEXT token to represent the static part before this tag
|
||||
auto openScope = [&](Clause c){ scope_.push (ParseCtx{c, currIDX()}); }; // start nested scope for bracketing construct (if / for)
|
||||
auto closeScope = [&] { scope_.pop(); }; // close innermost nested scope
|
||||
|
||||
auto linkElseToStart = [&]{ actions[beginIdx()].refIDX = currIDX(); }; // link the start position of the else-branch into opening logic code
|
||||
auto markJumpInScope = [&]{ scope_.top().after = currIDX(); }; // memorise jump before else-branch for later linkage
|
||||
auto linkLoopBack = [&]{ actions.back().refIDX = scope_.top().begin; }; // fill in the back-jump position at loop end
|
||||
auto linkJumpToNext = [&]{ actions[scope_.top().after].refIDX = currIDX(); }; // link jump to the position after the end of the logic bracket
|
||||
auto linkElseToStart = [&]{ actions[beginIdx()].refIDX = currIDX(); }; // link the start position of the else-branch into opening logic code
|
||||
auto markJumpInScope = [&]{ scope_.top().after = currIDX(); }; // memorise jump before else-branch for later linkage
|
||||
auto linkLoopBack = [&]{ actions.back().refIDX = scope_.top().begin; }; // fill in the back-jump position at loop end
|
||||
auto linkJumpToNext = [&]{ actions[scope_.top().after].refIDX = currIDX(); }; // link jump to the position after the end of the logic bracket
|
||||
|
||||
auto hasElse = [&]{ return scope_.top().after != 0; }; // a jump code to link was only marked if there was an else tag
|
||||
auto hasElse = [&]{ return scope_.top().after != 0; }; // a jump code to link was only marked if there was an else tag
|
||||
|
||||
using text_template::TagSyntax;
|
||||
|
||||
/* === Code Generation === */
|
||||
switch (parseIter->syntax) {
|
||||
|
|
@ -603,7 +614,7 @@ namespace lib {
|
|||
inline TextTemplate::ActionSeq
|
||||
TextTemplate::compile (string const& spec)
|
||||
{
|
||||
ActionSeq code = ActionCompiler().buildActions (parse (spec));
|
||||
ActionSeq code = ActionCompiler().buildActions (text_template::parse (spec));
|
||||
if (isnil (code))
|
||||
throw error::Invalid ("TextTemplate spec without active placeholders.");
|
||||
return code;
|
||||
|
|
@ -612,144 +623,138 @@ namespace lib {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ======= preconfigured data bindings ======= */
|
||||
|
||||
template<class DAT, typename SEL=void>
|
||||
struct TextTemplate::DataSource
|
||||
{
|
||||
static_assert (not sizeof(DAT),
|
||||
"unable to bind this data source "
|
||||
"for TextTemplate instantiation");
|
||||
|
||||
DataSource (DAT const&);
|
||||
};
|
||||
|
||||
using MapS = std::map<string,string>;
|
||||
|
||||
/**
|
||||
* Data-binding for a Map-of-strings.
|
||||
* Simple keys are retrieved by direct lookup.
|
||||
* For the representation of nested data sequences,
|
||||
* the following conventions apply
|
||||
* - the data sequence itself is represented by an index-key
|
||||
* - the value associated to this index-key is a CSV sequence
|
||||
* - each element in this sequence defines a key prefix
|
||||
* - nested keys are then defined as `<index-key>.<elm-key>.<key>`
|
||||
* - when key decoration is enabled for a nested data source, each
|
||||
* lookup for a given key is first tried with the prefix, then as-is.
|
||||
* Consequently, all data in the sequence must be present in the original
|
||||
* map, stored under the decorated keys.
|
||||
* @note multiply nested sequences are _not supported._
|
||||
* While it _is_ possible to have nested loops, the resulting sets
|
||||
* of keys must be disjoint and data must be present in the base map.
|
||||
* @see TextTemplate_test::verify_Map_binding()
|
||||
*/
|
||||
template<>
|
||||
struct TextTemplate::DataSource<MapS>
|
||||
{
|
||||
MapS const * data_{nullptr};
|
||||
string keyPrefix_{};
|
||||
|
||||
bool isNested() { return not isnil (keyPrefix_); }
|
||||
|
||||
DataSource() = default;
|
||||
DataSource(MapS const& map)
|
||||
: data_{&map}
|
||||
{ }
|
||||
|
||||
|
||||
using Value = std::string_view;
|
||||
using Iter = decltype(iterNestedKeys("",""));
|
||||
|
||||
bool
|
||||
contains (string key)
|
||||
{
|
||||
return (isNested() and util::contains (*data_, keyPrefix_+key))
|
||||
or util::contains (*data_, key);
|
||||
}
|
||||
|
||||
Value
|
||||
retrieveContent (string key)
|
||||
{
|
||||
MapS::const_iterator elm;
|
||||
if (isNested())
|
||||
{
|
||||
elm = data_->find (keyPrefix_+key);
|
||||
if (elm == data_->end())
|
||||
elm = data_->find (key);
|
||||
}
|
||||
else
|
||||
elm = data_->find (key);
|
||||
ENSURE (elm != data_->end());
|
||||
return elm->second;
|
||||
}
|
||||
|
||||
Iter
|
||||
getSequence (string key)
|
||||
{
|
||||
if (not contains(key))
|
||||
return Iter{};
|
||||
else
|
||||
return iterNestedKeys (key, retrieveContent(key));
|
||||
}
|
||||
|
||||
DataSource
|
||||
openContext (Iter& iter)
|
||||
{
|
||||
REQUIRE (iter);
|
||||
DataSource nested{*this};
|
||||
nested.keyPrefix_ += *iter;
|
||||
return nested;
|
||||
}
|
||||
};
|
||||
|
||||
using PairS = std::pair<string,string>;
|
||||
|
||||
template<>
|
||||
struct TextTemplate::DataSource<string>
|
||||
: TextTemplate::DataSource<MapS>
|
||||
{
|
||||
std::shared_ptr<MapS> spec_;
|
||||
|
||||
DataSource (string const& dataSpec)
|
||||
: spec_{new MapS}
|
||||
{
|
||||
data_ = spec_.get();
|
||||
explore (iterBindingSeq (dataSpec))
|
||||
.foreach([this](PairS const& bind){ spec_->insert (bind); });
|
||||
}
|
||||
namespace text_template {
|
||||
|
||||
template<class DAT, typename SEL>
|
||||
struct DataSource
|
||||
{
|
||||
static_assert (not sizeof(DAT),
|
||||
"unable to bind this data source "
|
||||
"for TextTemplate instantiation");
|
||||
|
||||
DataSource
|
||||
openContext (Iter& iter)
|
||||
{
|
||||
DataSource nested(*this);
|
||||
auto nestedBase = DataSource<MapS>::openContext (iter);
|
||||
nested.keyPrefix_ = nestedBase.keyPrefix_;
|
||||
return nested;
|
||||
}
|
||||
};
|
||||
|
||||
namespace {// help the compiler with picking the proper specialisation for the data binding
|
||||
DataSource (DAT const&);
|
||||
};
|
||||
|
||||
template<class STR, typename = meta::enable_if<meta::is_StringLike<STR>> >
|
||||
inline auto
|
||||
bindDataSource(STR const& spec)
|
||||
{
|
||||
return TextTemplate::DataSource<string>{spec};
|
||||
}
|
||||
using MapS = std::map<string,string>;
|
||||
|
||||
inline auto
|
||||
bindDataSource(MapS const& map)
|
||||
{
|
||||
return TextTemplate::DataSource<MapS>{map};
|
||||
}
|
||||
|
||||
/* Why this approach? couldn't we use CTAD?
|
||||
* - for one, there are various compiler bugs related to nested templates and CTAD
|
||||
* - moreover I am unable to figure out how to write a deduction guide for an
|
||||
* user provided specialisation, given possibly within another header.
|
||||
/**
|
||||
* Data-binding for a Map-of-strings.
|
||||
* Simple keys are retrieved by direct lookup.
|
||||
* For the representation of nested data sequences,
|
||||
* the following conventions apply
|
||||
* - the data sequence itself is represented by an index-key
|
||||
* - the value associated to this index-key is a CSV sequence
|
||||
* - each element in this sequence defines a key prefix
|
||||
* - nested keys are then defined as `<index-key>.<elm-key>.<key>`
|
||||
* - when key decoration is enabled for a nested data source, each
|
||||
* lookup for a given key is first tried with the prefix, then as-is.
|
||||
* Consequently, all data in the sequence must be present in the original
|
||||
* map, stored under the decorated keys.
|
||||
* @note multiply nested sequences are _not supported._
|
||||
* While it _is_ possible to have nested loops, the resulting sets
|
||||
* of keys must be disjoint and data must be present in the base map.
|
||||
* @see TextTemplate_test::verify_Map_binding()
|
||||
*/
|
||||
}
|
||||
template<>
|
||||
struct DataSource<MapS>
|
||||
{
|
||||
MapS const * data_{nullptr};
|
||||
string keyPrefix_{};
|
||||
|
||||
bool isSubScope() { return not isnil (keyPrefix_); }
|
||||
|
||||
DataSource() = default;
|
||||
DataSource(MapS const& map)
|
||||
: data_{&map}
|
||||
{ }
|
||||
|
||||
|
||||
using Value = std::string_view;
|
||||
using Iter = decltype(iterNestedKeys("",""));
|
||||
|
||||
bool
|
||||
contains (string key)
|
||||
{
|
||||
return (isSubScope() and util::contains (*data_, keyPrefix_+key))
|
||||
or util::contains (*data_, key);
|
||||
}
|
||||
|
||||
Value
|
||||
retrieveContent (string key)
|
||||
{
|
||||
MapS::const_iterator elm;
|
||||
if (isSubScope())
|
||||
{
|
||||
elm = data_->find (keyPrefix_+key);
|
||||
if (elm == data_->end())
|
||||
elm = data_->find (key);
|
||||
}
|
||||
else
|
||||
elm = data_->find (key);
|
||||
ENSURE (elm != data_->end());
|
||||
return elm->second;
|
||||
}
|
||||
|
||||
Iter
|
||||
getSequence (string key)
|
||||
{
|
||||
if (not contains(key))
|
||||
return Iter{};
|
||||
else
|
||||
return iterNestedKeys (key, retrieveContent(key));
|
||||
}
|
||||
|
||||
DataSource
|
||||
openContext (Iter& iter)
|
||||
{
|
||||
REQUIRE (iter);
|
||||
DataSource nested{*this};
|
||||
nested.keyPrefix_ += *iter;
|
||||
return nested;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
using PairS = std::pair<string,string>;
|
||||
|
||||
/** Adapter for the Map-Data-Source to consume a string spec (for testing) */
|
||||
template<>
|
||||
struct DataSource<string>
|
||||
: DataSource<MapS>
|
||||
{
|
||||
std::shared_ptr<MapS> spec_;
|
||||
|
||||
DataSource (string const& dataSpec)
|
||||
: spec_{new MapS}
|
||||
{
|
||||
data_ = spec_.get();
|
||||
explore (iterBindingSeq (dataSpec))
|
||||
.foreach([this](PairS const& bind){ spec_->insert (bind); });
|
||||
}
|
||||
|
||||
DataSource
|
||||
openContext (Iter& iter)
|
||||
{
|
||||
DataSource nested(*this);
|
||||
auto nestedBase = DataSource<MapS>::openContext (iter);
|
||||
nested.keyPrefix_ = nestedBase.keyPrefix_;
|
||||
return nested;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Deduction Guide: help the compiler with picking the proper specialisation
|
||||
* for a test-data source defined through a string spec or char literal
|
||||
*/
|
||||
template<class STR, typename = meta::enable_if<meta::is_StringLike<STR>> >
|
||||
DataSource(STR const&) -> DataSource<string>;
|
||||
|
||||
}// namespace text_template
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -834,7 +839,7 @@ namespace lib {
|
|||
TextTemplate::InstanceCore<SRC>::instantiateNext()
|
||||
{
|
||||
return actionIter_? actionIter_->instantiate(*this)
|
||||
: StrView{};
|
||||
: Value{};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -859,7 +864,7 @@ namespace lib {
|
|||
inline typename SRC::Value
|
||||
TextTemplate::InstanceCore<SRC>::getContent (string key)
|
||||
{
|
||||
static StrView nil{""};
|
||||
static Value nil{};
|
||||
return dataSrc_.contains(key)? dataSrc_.retrieveContent(key) : nil;
|
||||
}
|
||||
|
||||
|
|
@ -957,7 +962,7 @@ namespace lib {
|
|||
inline auto
|
||||
TextTemplate::submit (DAT const& data) const
|
||||
{
|
||||
return explore (InstanceCore{actions_, bindDataSource(data)});
|
||||
return explore (InstanceCore{actions_, text_template::DataSource(data)});
|
||||
}
|
||||
|
||||
/** submit data and materialise rendered results into a single string */
|
||||
|
|
|
|||
|
|
@ -780,7 +780,7 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
PLANNED "text template substitution" TextTemplate_test <<END
|
||||
TEST "text template substitution" TextTemplate_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -27,13 +27,9 @@
|
|||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"///////////////////////TODO
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/text-template.hpp"
|
||||
#include "lib/text-template-gen-node-binding.hpp"
|
||||
#include "lib/format-string.hpp"
|
||||
#include "lib/format-cout.hpp"///////////////////////TODO
|
||||
#include "lib/test/diagnostic-output.hpp"///////////////////////TODO
|
||||
#include "lib/stat/csv.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
|
@ -42,7 +38,9 @@ using std::regex_search;
|
|||
using std::smatch;
|
||||
using util::_Fmt;
|
||||
using util::join;
|
||||
using lib::diff::Rec;
|
||||
using lib::diff::MakeRec;
|
||||
using lib::diff::GenNode;
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
|
@ -51,6 +49,9 @@ namespace test {
|
|||
using MapS = std::map<string, string>;
|
||||
using LERR_(ITER_EXHAUST);
|
||||
|
||||
using text_template::ACCEPT_MARKUP;
|
||||
using text_template::TagSyntax;
|
||||
|
||||
|
||||
/***************************************************************************//**
|
||||
* @test verify a minimalistic text substitution engine with flexible
|
||||
|
|
@ -79,9 +80,7 @@ namespace test {
|
|||
}
|
||||
|
||||
|
||||
/** @test simple point-and-shot usage...
|
||||
* @todo WIP 4/24 ✔ define ⟶ ✔ implement
|
||||
*/
|
||||
/** @test simple point-and-shot usage... */
|
||||
void
|
||||
simpeUsage()
|
||||
{
|
||||
|
|
@ -100,7 +99,6 @@ namespace test {
|
|||
* - 3 ≙ end token
|
||||
* - 4 ≙ some logic token ("if" or "for")
|
||||
* - 5 ≙ a key or key path
|
||||
* @todo WIP 4/24 ✔ define ⟶ ✔ implement
|
||||
*/
|
||||
void
|
||||
verify_parsing()
|
||||
|
|
@ -197,7 +195,7 @@ namespace test {
|
|||
|
||||
|
||||
// Parse matches of this regexp into well defined syntax elements
|
||||
auto parser = parse (input);
|
||||
auto parser = text_template::parse (input);
|
||||
CHECK (not isnil(parser));
|
||||
CHECK (parser->syntax == TagSyntax::KEYID);
|
||||
CHECK (parser->lead == "one "_expect);
|
||||
|
|
@ -359,9 +357,7 @@ for} tail...
|
|||
|
||||
|
||||
|
||||
/** @test Compile a template and instantiate with various data bindings.
|
||||
* @todo WIP 4/24 ✔ define ⟶ ✔ implement
|
||||
*/
|
||||
/** @test Compile a template and instantiate with various data bindings. */
|
||||
void
|
||||
verify_instantiation()
|
||||
{
|
||||
|
|
@ -381,8 +377,7 @@ for} tail...
|
|||
|
||||
|
||||
/** @test Segments of the text-template can be included
|
||||
* conditionally, based on interpretation of a controlling key
|
||||
* @todo WIP 4/24 ✔ define ⟶ ✔ implement
|
||||
* conditionally, based on interpretation of a controlling key.
|
||||
*/
|
||||
void
|
||||
verify_conditional()
|
||||
|
|
@ -420,9 +415,8 @@ for} tail...
|
|||
* - for this test we use the Map-binding, which synthesises
|
||||
* key prefixes and expects bindings for those decorated keys
|
||||
* - typically, keys in inner scopes will shadow outer keys,
|
||||
* as is here demonstrated with the "x" key at top level
|
||||
* as is demonstrated here with the "x" key at top level
|
||||
* - loops and conditionals can be nested
|
||||
* @todo WIP 4/24 ✔ define ⟶ ✔ implement
|
||||
*/
|
||||
void
|
||||
verify_iteration()
|
||||
|
|
@ -450,8 +444,10 @@ for} tail...
|
|||
}
|
||||
|
||||
|
||||
/** @test TODO
|
||||
* @todo WIP 4/24 🔁 define ⟶ implement
|
||||
|
||||
/** @test build a data binding to a map-of-strings,
|
||||
* and verify all the operations used internally
|
||||
* by the text-template engine to navigate the data.
|
||||
*/
|
||||
void
|
||||
verify_Map_binding()
|
||||
|
|
@ -462,14 +458,14 @@ for} tail...
|
|||
,{"i.q.a","22"}
|
||||
,{"i.q.aa","222"}};
|
||||
|
||||
auto binding = bindDataSource (data);
|
||||
CHECK (meta::typeStr(binding) == "TextTemplate::DataSource<map<string, string>, void>"_expect );
|
||||
auto binding = text_template::DataSource{data};
|
||||
CHECK (meta::typeStr(binding) == "text_template::DataSource<map<string, string>, void>"_expect );
|
||||
CHECK ( binding.contains("a"));
|
||||
CHECK (not binding.contains("b"));
|
||||
CHECK (binding.retrieveContent("a") == "5"_expect );
|
||||
CHECK (binding.retrieveContent("i") == "p,q,r"_expect );
|
||||
CHECK (binding.retrieveContent("i.q.aa") == "222"_expect );
|
||||
CHECK (not binding.isNested());
|
||||
CHECK (not binding.isSubScope());
|
||||
|
||||
auto it = binding.getSequence("i");
|
||||
CHECK (it);
|
||||
|
|
@ -477,7 +473,7 @@ for} tail...
|
|||
CHECK (meta::typeStr(it) == "IterExplorer<IterableDecorator<string, CheckedCore<iter_explorer::Transformer<iter_explorer::BaseAdapter<RegexSearchIter>, string> > > >"_expect );
|
||||
|
||||
auto subBind = binding.openContext(it);
|
||||
CHECK (subBind.isNested());
|
||||
CHECK (subBind.isSubScope());
|
||||
CHECK ((meta::is_same<decltype(binding),decltype(subBind)>()));
|
||||
CHECK ( subBind.contains("a"));
|
||||
CHECK (not subBind.contains("b"));
|
||||
|
|
@ -494,7 +490,7 @@ for} tail...
|
|||
CHECK (subBind.retrieveContent("a") == "11"_expect );
|
||||
// ...rather need to open a new sub-ctx explicitly
|
||||
subBind = binding.openContext(it);
|
||||
CHECK (subBind.isNested());
|
||||
CHECK (subBind.isSubScope());
|
||||
CHECK (subBind.contains("a"));
|
||||
CHECK (subBind.contains("aa"));
|
||||
CHECK (subBind.retrieveContent("a") == "22"_expect );
|
||||
|
|
@ -519,8 +515,15 @@ for} tail...
|
|||
}
|
||||
|
||||
|
||||
/** @test TODO
|
||||
* @todo WIP 4/24 🔁 define ⟶ implement
|
||||
/** @test represent the same logical structure as in the
|
||||
* [preceding test](\ref #verify_Map_binding),
|
||||
* yet this time as a tree of GenNodes
|
||||
* - value bindings are translated into attribute access
|
||||
* - the iteration now requires an actually nested scope,
|
||||
* holding a sequence of child nodes
|
||||
* - each of these nodes constitutes a »data entity«
|
||||
* - when accessing keys from _within_ such a nested scope, attributes
|
||||
* of enclosing scopes are visible, unless shadowed by local definition.
|
||||
*/
|
||||
void
|
||||
verify_ETD_binding()
|
||||
|
|
@ -535,96 +538,63 @@ for} tail...
|
|||
.set("a", 22)
|
||||
.set("aa", 222)
|
||||
.genNode()
|
||||
, MakeRec()
|
||||
/*——empty——*/
|
||||
.genNode()
|
||||
))
|
||||
.genNode();
|
||||
SHOW_EXPR(root)
|
||||
auto binding = bindDataSource (root);
|
||||
SHOW_TYPE(decltype(binding))
|
||||
SHOW_EXPR(meta::typeStr(binding))
|
||||
SHOW_EXPR(binding.contains("a"))
|
||||
SHOW_EXPR(binding.contains("b"))
|
||||
SHOW_EXPR(binding.retrieveContent("a"))
|
||||
SHOW_EXPR(binding.retrieveContent("i"))
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
SHOW_EXPR(binding.retrieveContent("i.q.aa"))
|
||||
SHOW_EXPR(binding.isNested())
|
||||
CHECK (meta::typeStr(binding) == "TextTemplate::DataSource<map<string, string>, void>"_expect );
|
||||
|
||||
auto binding = text_template::DataSource{root};
|
||||
CHECK (meta::typeStr(binding) == "text_template::DataSource<GenNode, void>"_expect );
|
||||
CHECK ( binding.contains("a"));
|
||||
CHECK (not binding.contains("b"));
|
||||
CHECK (binding.retrieveContent("a") == "5"_expect );
|
||||
CHECK (binding.retrieveContent("i") == "p,q,r"_expect );
|
||||
CHECK (binding.retrieveContent("i.q.aa") == "222"_expect );
|
||||
CHECK (not binding.isNested());
|
||||
CHECK (binding.retrieveContent("a") == "5"_expect );
|
||||
CHECK (binding.retrieveContent("i") == "{|{a=11}, {a=22, aa=222}, {}}"_expect );
|
||||
CHECK (not binding.isSubScope());
|
||||
|
||||
auto it = binding.getSequence("i");
|
||||
SHOW_EXPR(meta::typeStr(it))
|
||||
SHOW_EXPR(bool(it))
|
||||
SHOW_EXPR(*it)
|
||||
CHECK (it);
|
||||
CHECK (*it == "i.p."_expect );
|
||||
CHECK (meta::typeStr(it) == "IterExplorer<IterableDecorator<string, CheckedCore<iter_explorer::Transformer<iter_explorer::BaseAdapter<RegexSearchIter>, string> > > >"_expect );
|
||||
CHECK (renderCompact(*it) == "{a=11}");
|
||||
CHECK (*it == root.data.get<Rec>().get("i").data.get<Rec>().child(0));
|
||||
|
||||
auto subBind = binding.openContext(it);
|
||||
SHOW_EXPR(meta::typeStr(subBind))
|
||||
SHOW_EXPR(bool(std::is_same<decltype(binding),decltype(subBind)>()))
|
||||
SHOW_EXPR(subBind.isNested())
|
||||
SHOW_EXPR(subBind.contains("a"))
|
||||
SHOW_EXPR(subBind.contains("b"))
|
||||
SHOW_EXPR(subBind.contains("aa"))
|
||||
SHOW_EXPR(subBind.contains("i"))
|
||||
SHOW_EXPR(subBind.retrieveContent("i"))
|
||||
SHOW_EXPR(subBind.retrieveContent("a"))
|
||||
CHECK (subBind.isNested());
|
||||
CHECK (subBind.isSubScope());
|
||||
CHECK ((meta::is_same<decltype(binding),decltype(subBind)>()));
|
||||
CHECK ( subBind.contains("a"));
|
||||
CHECK (not subBind.contains("b"));
|
||||
CHECK (not subBind.contains("aa"));
|
||||
CHECK ( subBind.contains("i"));
|
||||
CHECK (subBind.retrieveContent("i") == "p,q,r"_expect );
|
||||
CHECK (subBind.retrieveContent("a") == "11"_expect );
|
||||
CHECK (subBind.retrieveContent("i") == "{|{a=11}, {a=22, aa=222}, {}}"_expect );
|
||||
CHECK (subBind.retrieveContent("a") == "11"_expect );
|
||||
|
||||
++it;
|
||||
SHOW_EXPR(bool(it))
|
||||
SHOW_EXPR(*it)
|
||||
CHECK (it);
|
||||
CHECK (*it == "i.q."_expect );
|
||||
SHOW_EXPR(subBind.retrieveContent("a"))
|
||||
CHECK (subBind.retrieveContent("a") == "11"_expect );
|
||||
CHECK (renderCompact(*it) == "{a=22, aa=222}");
|
||||
CHECK (subBind.retrieveContent("a") == "11"_expect );
|
||||
|
||||
subBind = binding.openContext(it);
|
||||
SHOW_EXPR(subBind.isNested())
|
||||
SHOW_EXPR(subBind.contains("a"))
|
||||
SHOW_EXPR(subBind.contains("aa"))
|
||||
SHOW_EXPR(subBind.retrieveContent("a"))
|
||||
SHOW_EXPR(subBind.retrieveContent("aa"))
|
||||
SHOW_EXPR(subBind.retrieveContent("i.p.a"))
|
||||
SHOW_EXPR(subBind.retrieveContent("i.q.a"))
|
||||
CHECK (subBind.isNested());
|
||||
CHECK (subBind.isSubScope());
|
||||
CHECK (subBind.contains("a"));
|
||||
CHECK (subBind.contains("aa"));
|
||||
CHECK (subBind.retrieveContent("a") == "22"_expect );
|
||||
CHECK (subBind.retrieveContent("aa") == "222"_expect);
|
||||
CHECK (subBind.retrieveContent("i.p.a") == "11"_expect );
|
||||
CHECK (subBind.retrieveContent("i.q.a") == "22"_expect );
|
||||
CHECK (subBind.retrieveContent("a") == "22"_expect );
|
||||
CHECK (subBind.retrieveContent("aa") == "222"_expect);
|
||||
|
||||
++it;
|
||||
SHOW_EXPR(bool(it))
|
||||
CHECK (it);
|
||||
SHOW_EXPR(*it)
|
||||
CHECK (*it == "i.r."_expect );
|
||||
CHECK (renderCompact(*it) == "{}");
|
||||
|
||||
subBind = binding.openContext(it);
|
||||
SHOW_EXPR(subBind.contains("a"))
|
||||
SHOW_EXPR(subBind.contains("aa"))
|
||||
SHOW_EXPR(subBind.retrieveContent("a"))
|
||||
SHOW_EXPR(subBind.retrieveContent("i.p.a"))
|
||||
SHOW_EXPR(subBind.retrieveContent("i.q.a"))
|
||||
CHECK ( subBind.contains("a"));
|
||||
CHECK (not subBind.contains("aa"));
|
||||
CHECK (subBind.retrieveContent("a") == "5"_expect );
|
||||
CHECK (subBind.retrieveContent("i.p.a") == "11"_expect );
|
||||
CHECK (subBind.retrieveContent("i.q.a") == "22"_expect );
|
||||
CHECK (subBind.retrieveContent("a") == "5"_expect );
|
||||
|
||||
++it;
|
||||
SHOW_EXPR(bool(it))
|
||||
CHECK (isnil (it));
|
||||
VERIFY_ERROR (ITER_EXHAUST, *it);
|
||||
#endif
|
||||
|
||||
|
||||
TextTemplate tt{"${for i}a=${a} ${if aa}and aa=${aa} ${endif}${endfor}."};
|
||||
CHECK (tt.render(root) == "a=11 a=22 and aa=222 a=5 ."_expect);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -112472,8 +112472,8 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710723528262" ID="ID_1806943398" MODIFIED="1710723539822" TEXT="minimale Templating-Funktionalität bereitstellen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1710723528262" ID="ID_1806943398" MODIFIED="1711589327124" TEXT="minimale Templating-Funktionalität bereitstellen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1710723556427" ID="ID_967894122" MODIFIED="1710723653752" TEXT="Baukastensystem — kein Framework">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
|
|
@ -112496,13 +112496,21 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</html></richcontent>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1710792755709" ID="ID_952834241" MODIFIED="1711336122669" TEXT="ein situativ-funktionales Daten-Binding">
|
||||
<node COLOR="#435e98" CREATED="1710792755709" ID="ID_952834241" MODIFIED="1711589315492">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
ein situativ-funktionales <b>Daten-Binding</b>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710792833458" ID="ID_275925925" MODIFIED="1710792878693" TEXT="ein generischer Standard-Adapter für map-artigen Zugriff">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1710792833458" ID="ID_275925925" MODIFIED="1711589302822" TEXT="ein generischer Standard-Adapter für map-artigen Zugriff">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710792883983" ID="ID_674930846" MODIFIED="1710793004552" TEXT="ein Adapter für Lumiera ETD mit util::toString-Konvertierung">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1710792883983" ID="ID_674930846" MODIFIED="1711589302822" TEXT="ein Adapter für Lumiera ETD mit util::toString-Konvertierung">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1710793061549" ID="ID_896868325" MODIFIED="1711336391576" TEXT="Implementierung">
|
||||
|
|
@ -112521,8 +112529,8 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#2a0fad" CREATED="1710793119597" ID="ID_1328122626" MODIFIED="1711336460037" TEXT="Design">
|
||||
<font BOLD="true" NAME="SansSerif" SIZE="14"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1710793135490" ID="ID_1136896163" MODIFIED="1710811869814" TEXT="Binding">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1710793135490" ID="ID_1136896163" MODIFIED="1711589262694" TEXT="Binding">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1710793140948" ID="ID_1992696242" MODIFIED="1711336422206" TEXT="Schema für Binding anlegen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
|
|
@ -112618,8 +112626,8 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710793579630" ID="ID_1056577943" MODIFIED="1710793609484" TEXT="direktes λ-Binding für Test">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1710793579630" ID="ID_1056577943" MODIFIED="1711589260474" TEXT="Variante des Map-Bindings für einfache Test-Spec">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1710793178661" ID="ID_1043138581" MODIFIED="1711336174610" TEXT="Parser">
|
||||
|
|
@ -112661,8 +112669,8 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1710799898735" ID="ID_965266428" MODIFIED="1711311776315" TEXT="Datenstruktur">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1710799898735" FOLDED="true" ID="ID_965266428" MODIFIED="1711589280713" TEXT="Datenstruktur">
|
||||
<icon BUILTIN="info"/>
|
||||
<node COLOR="#338800" CREATED="1710799920412" ID="ID_908650592" MODIFIED="1711311774454" TEXT="parsing">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1710800526514" ID="ID_989913764" MODIFIED="1711311772980" TEXT="aufzubauender InstaceCode">
|
||||
|
|
@ -112736,8 +112744,9 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
<node COLOR="#338800" CREATED="1710799996889" ID="ID_1910948400" MODIFIED="1711336380860" TEXT="Processing">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1710799920412" ID="ID_1195498496" MODIFIED="1711323991738" TEXT="parsing">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1710799920412" FOLDED="true" ID="ID_1195498496" MODIFIED="1711589227161" TEXT="parsing">
|
||||
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="info"/>
|
||||
<node COLOR="#338800" CREATED="1710804975672" ID="ID_1778438879" MODIFIED="1711311749543" TEXT="Reg-Exp-Iteration auf die Tag-Syntax">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1711211822654" FOLDED="true" ID="ID_1575734676" LINK="#ID_1871157756" MODIFIED="1711212214143" TEXT="Layer-1 : Übersetzung in Syntax-Fälle">
|
||||
|
|
@ -112857,8 +112866,9 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1710799932570" ID="ID_1103663065" MODIFIED="1711336375053" TEXT="instance">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1710799932570" FOLDED="true" ID="ID_1103663065" MODIFIED="1711589227159" TEXT="instance">
|
||||
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="info"/>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1710800667346" ID="ID_299824445" MODIFIED="1711336365428" TEXT="handzuhabende Situationen">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node COLOR="#435e98" CREATED="1710800705306" ID="ID_906431564" MODIFIED="1711326090062" TEXT="literaler Textblock">
|
||||
|
|
@ -112928,7 +112938,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1710856691780" ID="ID_763640380" MODIFIED="1711336351889" TEXT="Rahmen schaffen">
|
||||
<node COLOR="#338800" CREATED="1710856691780" ID="ID_763640380" MODIFIED="1711336351889" TEXT="Implementierung aufbauen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1710856716918" ID="ID_1752947576" MODIFIED="1710856723204" TEXT="rückwärts vorgehen">
|
||||
<icon BUILTIN="idea"/>
|
||||
|
|
@ -113479,9 +113489,9 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1710793352301" ID="ID_1691061928" MODIFIED="1711335701805" TEXT="Bindings">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1710793356309" ID="ID_618241969" MODIFIED="1711335699916" TEXT="Map">
|
||||
<node COLOR="#338800" CREATED="1710793352301" ID="ID_1691061928" MODIFIED="1711589154103" TEXT="Bindings">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1710793356309" FOLDED="true" ID="ID_618241969" MODIFIED="1711335699916" TEXT="Map">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1710804031539" ID="ID_1692068622" MODIFIED="1710879172991" TEXT="der Inhaltstyp wird via util::toString() zugegriffen"/>
|
||||
<node CREATED="1710804062496" ID="ID_377899149" MODIFIED="1710804093937" TEXT="DataSrc ist ein generischer Wrapper um die Map"/>
|
||||
|
|
@ -113541,7 +113551,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1710804833930" ID="ID_1766619477" MODIFIED="1711475112219" TEXT="Test-Binding">
|
||||
<node COLOR="#338800" CREATED="1710804833930" FOLDED="true" ID="ID_1766619477" MODIFIED="1711475112219" TEXT="Test-Binding">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1710804841699" ID="ID_933127899" MODIFIED="1710804917699" TEXT="eine String-Spec mit kommaseparierten key=val -Literalen"/>
|
||||
<node CREATED="1710804918648" ID="ID_1642910022" MODIFIED="1710804947480" TEXT="wird einmal geparst und in eine Map gepackt ⟶ Map-Binding">
|
||||
|
|
@ -113565,7 +113575,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711385634139" ID="ID_1398666279" MODIFIED="1711385663177" TEXT="der Datenquellen-Typ DAT ist direkt angegeben">
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#ccb37e" COLOR="#7614dc" CREATED="1711385647409" ID="ID_61608130" MODIFIED="1711416564386" TEXT="er wird daher literal vom Argument-Typ abgegriffen">
|
||||
<node BACKGROUND_COLOR="#ccb37e" COLOR="#7614dc" CREATED="1711385647409" FOLDED="true" ID="ID_61608130" MODIFIED="1711416564386" TEXT="er wird daher literal vom Argument-Typ abgegriffen">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1711406517514" ID="ID_1242921809" MODIFIED="1711406555810">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
|
|
@ -113598,8 +113608,9 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
<node CREATED="1711406706369" ID="ID_1041764555" MODIFIED="1711406720779" TEXT="weiteres Problem: bekannte Bugs im GCC-8 für nested Types"/>
|
||||
<node COLOR="#338800" CREATED="1711406725495" ID="ID_1509991147" MODIFIED="1711416586404" TEXT="bessere Lösung: verwende einen extension-Point">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#5b280f" CREATED="1711406725495" ID="ID_1509991147" MODIFIED="1711587514531" TEXT="bessere Lösung: verwende einen extension-Point">
|
||||
<linktarget COLOR="#ee185e" DESTINATION="ID_1509991147" ENDARROW="Default" ENDINCLINATION="-1069;58;" ID="Arrow_ID_1743534857" SOURCE="ID_1870060928" STARTARROW="None" STARTINCLINATION="-243;-15;"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1711416020912" ID="ID_843629573" MODIFIED="1711416500243" TEXT="grrr... funktioniert auch nur so halbwegs">
|
||||
<icon BUILTIN="smily_bad"/>
|
||||
</node>
|
||||
|
|
@ -113633,6 +113644,20 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#c2179b" CREATED="1711416468699" ID="ID_1423682198" MODIFIED="1711416581308" TEXT="...und ich habe hier schon viel zu viel Zeit verbracht">
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="11"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#ddce7e" COLOR="#fa002a" CREATED="1711587435961" HGAP="-2" ID="ID_671821876" MODIFIED="1711587487457" TEXT="Inferenz funktioniert nicht für nachträglich bereitgestellte Spezialisierungen" VSHIFT="20">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#690f40" CREATED="1711587525536" ID="ID_271753506" MODIFIED="1711587575547" TEXT="⟹ ein Deduction-Guide ist die einzige (saubere) Lösung">
|
||||
<node COLOR="#338800" CREATED="1711587580164" ID="ID_332093875" MODIFIED="1711587633227" TEXT="dann muß ich eben auf die verschachtelte Klasse verzichten">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node CREATED="1711587594335" ID="ID_1406188031" MODIFIED="1711587608968" TEXT="grrr..... daß ich SO LANGE dafür gebraucht habe">
|
||||
<icon BUILTIN="smily_bad"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1711587610285" ID="ID_1850551957" MODIFIED="1711587625162" TEXT="es wird so viel einfacher und klarer dadurch">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -113640,15 +113665,16 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1710793367246" ID="ID_1695880616" MODIFIED="1711508518124" TEXT="ETD">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710804556008" ID="ID_1997691101" MODIFIED="1711475133506" TEXT="DataSrc repräsentiert ein »Objekt« ≙ Rec<GenNode>">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1711475218638" ID="ID_44068114" MODIFIED="1711475231130" TEXT="Benötigte Eigenschaften">
|
||||
<node COLOR="#338800" CREATED="1710793367246" FOLDED="true" ID="ID_1695880616" MODIFIED="1711589162596" TEXT="ETD">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1710804556008" ID="ID_1997691101" MODIFIED="1711587647262" TEXT="DataSrc repräsentiert ein »Objekt« ≙ Rec<GenNode>">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1711475218638" ID="ID_44068114" MODIFIED="1711587651816" TEXT="Benötigte Eigenschaften">
|
||||
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1711475232644" ID="ID_315057671" MODIFIED="1711475241511" TEXT="muß auf Attribute per Key zugreifen können"/>
|
||||
<node CREATED="1711475244323" ID="ID_1323303321" MODIFIED="1711475260460" TEXT="muß für einen solchen Zugriff eine String-Repräsentation liefern">
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1711475264023" ID="ID_1962088901" MODIFIED="1711497355791" TEXT="das ist ein kritischer Punkt für dieses Binding">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711475264023" ID="ID_1962088901" MODIFIED="1711574657090" TEXT="das ist ein kritischer Punkt für dieses Binding">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711475279862" HGAP="21" ID="ID_392726526" MODIFIED="1711497352861" STYLE="bubble" VSHIFT="-4">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
|
|
@ -113673,7 +113699,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<node CREATED="1711475487018" ID="ID_240701708" MODIFIED="1711475497215" TEXT="aber GenNode hat eine String-Repräsentation">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1711475501474" ID="ID_1956601098" MODIFIED="1711475509907" TEXT="das gilt auch für den »DataCap«">
|
||||
<node CREATED="1711475501474" FOLDED="true" ID="ID_1956601098" MODIFIED="1711475509907" TEXT="das gilt auch für den »DataCap«">
|
||||
<node CREATED="1711475606554" ID="ID_716867341" MODIFIED="1711475616097" TEXT="der beruht auf einer lib::Variant"/>
|
||||
<node CREATED="1711475617289" ID="ID_653285103" MODIFIED="1711475627315" TEXT="und hat einen eingebetteten Buffer"/>
|
||||
<node CREATED="1711475628101" ID="ID_89556720" MODIFIED="1711475672880">
|
||||
|
|
@ -113686,23 +113712,114 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1711475675289" ID="ID_369134454" MODIFIED="1711477410646" TEXT="für GenNode ist das mit einem Variant-Visitor hinterlegt?">
|
||||
<node COLOR="#435e98" CREATED="1711475675289" ID="ID_369134454" MODIFIED="1711588947418" TEXT="für GenNode ist das mit einem Variant-Visitor hinterlegt?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1711477412790" ID="ID_512513677" MODIFIED="1711477429494" TEXT="gen-node.cpp : DataCap::operator string()"/>
|
||||
<node CREATED="1711477430263" ID="ID_931178080" MODIFIED="1711477462879" TEXT="delegiert an Variant<DataValues>::operator string()"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1711477398892" ID="ID_130677787" MODIFIED="1711477476253" TEXT="ich finde den nicht??">
|
||||
<icon BUILTIN="flag-pink"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1711477477202" ID="ID_1417186531" MODIFIED="1711477489600" TEXT="wo versteckt sich der Wicht?">
|
||||
<node COLOR="#435e98" CREATED="1711477398892" ID="ID_130677787" MODIFIED="1711560936386" TEXT="ich finde den nicht??">
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1711477477202" ID="ID_1417186531" MODIFIED="1711560919463" TEXT="wo versteckt sich der Wicht?">
|
||||
<arrowlink COLOR="#362761" DESTINATION="ID_643794778" ENDARROW="Default" ENDINCLINATION="49;0;" ID="Arrow_ID_1311257156" STARTARROW="None" STARTINCLINATION="2;10;"/>
|
||||
<icon BUILTIN="smiley-oh"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1711560945653" ID="ID_1382282875" MODIFIED="1711560953232" TEXT="also: kein Variant-Visitor hier"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711477500254" ID="ID_403443386" MODIFIED="1711507689410" TEXT="man könnte es ganz einfach mal »pragmatisch« nutzen">
|
||||
<linktarget COLOR="#f83362" DESTINATION="ID_403443386" ENDARROW="Default" ENDINCLINATION="-604;28;" ID="Arrow_ID_1274881948" SOURCE="ID_285017359" STARTARROW="None" STARTINCLINATION="409;-28;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1711477520228" ID="ID_1881261418" MODIFIED="1711477535488" TEXT="und so herausfinden wo es implementiert ist">
|
||||
<node COLOR="#435e98" CREATED="1711477500254" FOLDED="true" ID="ID_403443386" MODIFIED="1711588956778" TEXT="man könnte es ganz einfach mal »pragmatisch« nutzen">
|
||||
<linktarget COLOR="#4733f8" DESTINATION="ID_403443386" ENDARROW="Default" ENDINCLINATION="-604;28;" ID="Arrow_ID_1274881948" SOURCE="ID_285017359" STARTARROW="None" STARTINCLINATION="409;-28;"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node COLOR="#435e98" CREATED="1711477520228" ID="ID_1881261418" MODIFIED="1711560701334" TEXT="und so herausfinden wo es implementiert ist">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1711560706492" ID="ID_643794778" MODIFIED="1711560921661" TEXT="ganz unten in variant.hpp">
|
||||
<linktarget COLOR="#362761" DESTINATION="ID_643794778" ENDARROW="Default" ENDINCLINATION="49;0;" ID="Arrow_ID_1311257156" SOURCE="ID_1417186531" STARTARROW="None" STARTINCLINATION="2;10;"/>
|
||||
</node>
|
||||
<node CREATED="1711560715406" ID="ID_446846607" MODIFIED="1711560742376" TEXT="dieser Operator kann nämlich in diesem Zusammenhang generisch implementiert werden"/>
|
||||
<node CREATED="1711560743856" ID="ID_978230403" MODIFIED="1711560765542" TEXT="...weil er auf einer explizit getypten Subklasse (mit VTable) sitzt">
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1711560786602" ID="ID_825115031" MODIFIED="1711560819757" STYLE="bubble">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
<font face="Monospaced" size="2" color="#660a0a">  template<typename TYPES> </font>
|
||||
</p>
|
||||
<p>
|
||||
<font face="Monospaced" size="2" color="#660a0a">  template<typename TY> </font>
|
||||
</p>
|
||||
<p>
|
||||
<font face="Monospaced" size="2" color="#660a0a">  Variant<TYPES>::Buff<TY>::operator string()  const </font>
|
||||
</p>
|
||||
<p>
|
||||
<font face="Monospaced" size="2" color="#660a0a">  { </font>
|
||||
</p>
|
||||
<p>
|
||||
<font face="Monospaced" size="2" color="#660a0a">    return util::typedString (this->access()); </font>
|
||||
</p>
|
||||
<p>
|
||||
<font face="Monospaced" size="2" color="#660a0a">  } </font>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="list"/>
|
||||
</node>
|
||||
<node COLOR="#a00fd0" CREATED="1711560832345" ID="ID_156349199" MODIFIED="1711560863518">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
OK ... <i>nicht ganz das was wir brauchen</i>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1711560972306" FOLDED="true" ID="ID_719053806" MODIFIED="1711588960886" TEXT="also müssen wir selber einen Variant-Visitor bereitstellen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1711564049437" ID="ID_487217024" MODIFIED="1711564247198" TEXT="da gibt es gewisse Beschränkungen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
...seinerzeit bin ich da mit meinem Design an Grenzen gestoßen und konnte (oder wollte) mich nicht aus der Zwanslage befreien; das Problem ist, daß die Dispatch-Funktion selber wieder eine virtuelle Funktion sein muß, die auf der »inneren Hülle« des Variant-Record definiert ist (damit dann die äußere Hülle ohne weitere Vorkenntnisse den Visitor nach innen schieben kann). Mit dem bestehenden Code ist man also in der Signatur des Visitors nicht frei, sondern hat nur wenige, festgelegte Varianten zus Auswahl (für die jeweils ein kompletter Pfad nach innen definiert ist).
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1711565889294" ID="ID_1314620188" MODIFIED="1711565895271" TEXT="#1361 re-assess Variant implementation">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
</node>
|
||||
<node CREATED="1711565911936" ID="ID_982437785" MODIFIED="1711565926006" TEXT="man könnte ggfs den inneren Container nochmal neu programmieren"/>
|
||||
<node CREATED="1711565926946" ID="ID_1030763261" MODIFIED="1711565939628" TEXT="und dabei von Grund auf per Loki-List generieren lassen"/>
|
||||
<node CREATED="1711565940736" ID="ID_356987645" MODIFIED="1711566251775" TEXT="vielleicht geht da noch mehr mit C++23 ...?"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" CREATED="1711568655251" ID="ID_555820807" MODIFIED="1711568734447">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
habe eine dritte Visitor-Variante eingeführt: den »<font face="Monospaced" color="#490fb9">Renderer</font>«
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
das ist dann ein <font face="Monospaced" color="#6b1616">VisitorConstFunc<string></font>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1711568748391" ID="ID_1280919071" MODIFIED="1711574589702" TEXT="brauche aber ein Kompakt-Format für nested-Scopes">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1711568763197" ID="ID_11717336" MODIFIED="1711568775472" TEXT="eigentlich brauche ich hier etwas Generisches"/>
|
||||
<node CREATED="1711568793211" ID="ID_1498542681" MODIFIED="1711568817472" TEXT="⟹ die Grund-Funktionalität sollte es eigentlich auf GenNode geben"/>
|
||||
<node COLOR="#435e98" CREATED="1711574621739" ID="ID_523565474" MODIFIED="1711574651311" TEXT="(trotz gewisser Bedenken — nun offiziell für GenNode eingeführt...)">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -113724,7 +113841,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<node CREATED="1711486798598" ID="ID_1504667047" MODIFIED="1711488886985" TEXT="d.h. wieder der shared-ptr-Trick"/>
|
||||
<node CREATED="1711486786808" ID="ID_1943118433" MODIFIED="1711486796658" TEXT="das ist bei der Map-aus-string-spect genauso"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711491153943" ID="ID_561443056" MODIFIED="1711491282329" TEXT="noch besser: Ergebnistyp dynamisieren">
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1711491153943" ID="ID_561443056" MODIFIED="1711574672221" TEXT="noch besser: Ergebnistyp dynamisieren">
|
||||
<icon BUILTIN="forward"/>
|
||||
<node CREATED="1711491167092" ID="ID_679038844" MODIFIED="1711491179598" TEXT="dem Iterations-Mechanismus ist dieser Typ nämlich scheißegal"/>
|
||||
<node CREATED="1711491180498" ID="ID_1200622160" MODIFIED="1711491194500" TEXT="und da die InstanceCore ohnehin geTemplated ist...."/>
|
||||
|
|
@ -113733,9 +113850,10 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711475137705" ID="ID_265880025" MODIFIED="1711475154240" TEXT="Implementierung der Repräsentation">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1711491795547" ID="ID_1481897171" MODIFIED="1711491809910" TEXT="Einstiegspunkt für einen Scope ist stets eine GenNode">
|
||||
<node COLOR="#338800" CREATED="1711475137705" ID="ID_265880025" MODIFIED="1711588884021" TEXT="Implementierung der Repräsentation">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1711491795547" FOLDED="true" ID="ID_1481897171" MODIFIED="1711589067976" TEXT="Einstiegspunkt für einen Scope ist stets eine GenNode">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1711491812270" ID="ID_234227882" MODIFIED="1711501542307" TEXT="man könnte das auch genau anders herum handhaben">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
|
@ -113767,7 +113885,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<node CREATED="1711491953690" ID="ID_477686075" MODIFIED="1711491964956" TEXT="dann läßt sich leicht eine Ref-GenNode bauen"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#5b280f" CREATED="1711492643022" ID="ID_149060446" MODIFIED="1711504228004" TEXT="als Abkürzung / Cache wird ein sekundärer Rec-Pointer mitgeführt">
|
||||
<node COLOR="#5b280f" CREATED="1711492643022" FOLDED="true" ID="ID_149060446" MODIFIED="1711504228004" TEXT="als Abkürzung / Cache wird ein sekundärer Rec-Pointer mitgeführt">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1711492667078" ID="ID_502425157" MODIFIED="1711492672470" TEXT="dieser kann NULL sein"/>
|
||||
<node CREATED="1711492673202" ID="ID_1566411442" MODIFIED="1711492715043" TEXT="⟹ dann ist klar daß das Target value-GenNode ist"/>
|
||||
|
|
@ -113799,7 +113917,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1711492744185" ID="ID_1414659694" MODIFIED="1711493061959" TEXT="zudem brauchen wir einen parent-Scope">
|
||||
<node COLOR="#435e98" CREATED="1711492744185" FOLDED="true" ID="ID_1414659694" MODIFIED="1711589054015" TEXT="zudem brauchen wir einen parent-Scope">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1711493190221" ID="ID_1049043334" MODIFIED="1711493218882" TEXT="hier wird die Sache brandgefährlich">
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
|
|
@ -113817,6 +113935,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#338800" CREATED="1711493364914" ID="ID_1017484098" MODIFIED="1711499112194" TEXT="sinnvollerweise müßte man das als Teil des Konzepts betrachten">
|
||||
<linktarget COLOR="#fefbd2" DESTINATION="ID_1017484098" ENDARROW="Default" ENDINCLINATION="-607;27;" ID="Arrow_ID_443411509" SOURCE="ID_680558791" STARTARROW="None" STARTINCLINATION="102;-6;"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1711499125824" ID="ID_727245116" MODIFIED="1711499133531" TEXT="InstanceCore<SRC>::focusNested()"/>
|
||||
<node CREATED="1711499134805" ID="ID_1070301223" MODIFIED="1711499142771" TEXT="bestehender Code, aber klarer formuliert"/>
|
||||
|
|
@ -113835,19 +113954,20 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711499198944" ID="ID_509882790" MODIFIED="1711499229342" TEXT="man könnte damit nun sogar die Map-Implementierung beliebig »virtuell schachtelbar« machen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1711499198944" ID="ID_509882790" MODIFIED="1711588876069" TEXT="man könnte damit nun sogar die Map-Implementierung beliebig »virtuell schachtelbar« machen">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711499245448" ID="ID_1914030460" MODIFIED="1711504682259" TEXT="dieser wird durch einen DataSrc-Pointer repräsentiert">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1711499245448" ID="ID_1914030460" MODIFIED="1711587674044" TEXT="dieser wird durch einen DataSrc-Pointer repräsentiert">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1711504691401" ID="ID_36424118" MODIFIED="1711504724028" TEXT="tail-delegation für Attribut-Zugriffe"/>
|
||||
<node COLOR="#435e98" CREATED="1711504724929" ID="ID_792000097" MODIFIED="1711504739272" TEXT="erlaubt somit beliebige Schachtelung">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1711499521659" ID="ID_1374441904" MODIFIED="1711499539721" TEXT="wo ansiedeln?">
|
||||
<node COLOR="#435e98" CREATED="1711499521659" FOLDED="true" ID="ID_1374441904" MODIFIED="1711589055699" TEXT="wo ansiedeln?">
|
||||
<font NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1711499541233" ID="ID_1123960201" MODIFIED="1711499563146" TEXT="man könnte das Data-Binding auch in gen-node.hpp legen"/>
|
||||
|
|
@ -113874,12 +113994,14 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1710804591691" ID="ID_1815976275" MODIFIED="1710804603973" TEXT="Key-Zugriff wird übersetzt in Attribut-Zugriff">
|
||||
<node COLOR="#338800" CREATED="1710804591691" ID="ID_1815976275" MODIFIED="1711589100685" TEXT="Key-Zugriff wird übersetzt in Attribut-Zugriff">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1711469498958" ID="ID_106772989" MODIFIED="1711469520804" TEXT="kann kaskadieren in einen parent-Scope"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1711469521509" ID="ID_680558791" MODIFIED="1711506649812" TEXT="dafür wird ein optionaler Rec<GenNode>* verwendet">
|
||||
<icon BUILTIN="help"/>
|
||||
<node COLOR="#435e98" CREATED="1711469521509" ID="ID_680558791" MODIFIED="1711589043366" TEXT="dafür wird ein Pointer auf die Parent-DataSrc verwendet">
|
||||
<arrowlink COLOR="#fefbd2" DESTINATION="ID_1017484098" ENDARROW="Default" ENDINCLINATION="-607;27;" ID="Arrow_ID_443411509" STARTARROW="None" STARTINCLINATION="102;-6;"/>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1711506650791" ID="ID_1486045472" MODIFIED="1711506683627" TEXT="Generischer Zugriff widerspricht dem GenNode-Design">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711506650791" ID="ID_1486045472" MODIFIED="1711589095295" TEXT="Generischer Zugriff widerspricht dem GenNode-Design">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1711506695185" ID="ID_133207964" MODIFIED="1711506719546" TEXT="muß bereits wissen, daß in der GenNode ein Record liegt"/>
|
||||
<node CREATED="1711507563671" ID="ID_260828349" MODIFIED="1711507583155" TEXT="verwende eine Hilfsmethode findNode(key)">
|
||||
|
|
@ -113888,13 +114010,14 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<node CREATED="1711507611702" ID="ID_1556736224" MODIFIED="1711507619619" TEXT="und kaskadiert in den Parent-Context"/>
|
||||
<node CREATED="1711507620461" ID="ID_1478877809" MODIFIED="1711507626101" TEXT="kann nullptr zurückliefern"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711507631097" ID="ID_285017359" MODIFIED="1711507694404" TEXT="nächstes Problem �� Rendern als String">
|
||||
<arrowlink COLOR="#f83362" DESTINATION="ID_403443386" ENDARROW="Default" ENDINCLINATION="-604;28;" ID="Arrow_ID_1274881948" STARTARROW="None" STARTINCLINATION="409;-28;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#435e98" CREATED="1711507631097" ID="ID_285017359" MODIFIED="1711589090991" TEXT="nächstes Problem �� Rendern als String">
|
||||
<arrowlink COLOR="#4733f8" DESTINATION="ID_403443386" ENDARROW="Default" ENDINCLINATION="-604;28;" ID="Arrow_ID_1274881948" STARTARROW="None" STARTINCLINATION="409;-28;"/>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1710804616944" ID="ID_538897899" MODIFIED="1710804626706" TEXT="Iteration wird übersetzt in Scope-Iteration">
|
||||
<node COLOR="#338800" CREATED="1710804616944" ID="ID_538897899" MODIFIED="1711589102601" TEXT="Iteration wird übersetzt in Scope-Iteration">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711469668314" ID="ID_1640263666" MODIFIED="1711469688642" TEXT="hier ist eine zusätzliche Binding-Konvention notwendig">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1711469692623" ID="ID_210919622" MODIFIED="1711469701114" TEXT="der der Scope ist ein vector<GenNode>"/>
|
||||
|
|
@ -113940,45 +114063,55 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1710804633837" ID="ID_1119504137" MODIFIED="1711470052179" TEXT="normalerweise ist Element im Scope wieder ein Rec<GenNode>">
|
||||
<node COLOR="#338800" CREATED="1710804633837" ID="ID_1119504137" MODIFIED="1711589108260" TEXT="normalerweise ist Element im Scope wieder ein Rec<GenNode>">
|
||||
<linktarget COLOR="#4d81bd" DESTINATION="ID_1119504137" ENDARROW="Default" ENDINCLINATION="5;-35;" ID="Arrow_ID_279787572" SOURCE="ID_1900618698" STARTARROW="None" STARTINCLINATION="-46;4;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1710804715954" ID="ID_1537394187" MODIFIED="1711470077889" TEXT="verschachtelte Scopes können aber leer sein (wegen Parent-Delegation)"/>
|
||||
<node CREATED="1711508494304" ID="ID_1420529551" MODIFIED="1711508511954" TEXT="das wird vom code jedoch automatisch (und transparent) gehandhabt">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1711508533490" ID="ID_1740637248" MODIFIED="1711560355372" TEXT="Test">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1711508533490" ID="ID_1740637248" MODIFIED="1711589115029" TEXT="Test">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#ff00cb" CREATED="1711508538010" ID="ID_1041108883" MODIFIED="1711508554635" TEXT="puh....">
|
||||
<font NAME="SansSerif" SIZE="11"/>
|
||||
<icon BUILTIN="smiley-neutral"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1711508556799" ID="ID_681566820" MODIFIED="1711560360996" TEXT="erst mal schrittweise herantasten...">
|
||||
<node COLOR="#338800" CREATED="1711508556799" ID="ID_681566820" MODIFIED="1711589117232" TEXT="erst mal schrittweise herantasten...">
|
||||
<arrowlink COLOR="#236cc2" DESTINATION="ID_778488140" ENDARROW="Default" ENDINCLINATION="97;-12;" ID="Arrow_ID_1117346354" STARTARROW="None" STARTINCLINATION="67;272;"/>
|
||||
<icon BUILTIN="pencil"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1711508602973" ID="ID_1154919226" MODIFIED="1711560365246" TEXT="einen Beispiel-Daten-Kontext bereitstellen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1711508585131" ID="ID_1692560421" MODIFIED="1711560367785" TEXT="die DataSource explizit erzeugen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1711508631813" ID="ID_464128354" MODIFIED="1711560375265" TEXT="die Zugriffsmethoden durchspielen">
|
||||
<icon BUILTIN="pencil"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711508645715" ID="ID_1542705162" MODIFIED="1711508654315" TEXT="dann weiter zur Iteration">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1711560334047" ID="ID_101340479" MODIFIED="1711560352394" STYLE="fork" TEXT="brauche dedizierten String-Renderer">
|
||||
<node COLOR="#338800" CREATED="1711508631813" ID="ID_464128354" MODIFIED="1711574686943" TEXT="die Zugriffsmethoden durchspielen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#435e98" CREATED="1711560334047" ID="ID_101340479" MODIFIED="1711589129945" STYLE="fork" TEXT="brauche dedizierten String-Renderer">
|
||||
<font NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1711508645715" FOLDED="true" ID="ID_1542705162" MODIFIED="1711589148232" TEXT="dann weiter zur Iteration">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#435e98" CREATED="1711575833497" ID="ID_1154225807" MODIFIED="1711587366654" TEXT="Attribut im Parent-Scope nicht gefunden">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1711575848727" ID="ID_510927924" MODIFIED="1711575859369" TEXT="Fehler in der if-else-Logik"/>
|
||||
<node CREATED="1711575861213" ID="ID_1720425510" MODIFIED="1711575885596" TEXT="die ersten beiden Zweige sind exclusiv, der dritte muß danach versucht werden"/>
|
||||
</node>
|
||||
<node COLOR="#5b280f" CREATED="1711587369703" ID="ID_1870060928" MODIFIED="1711587514531" TEXT="das Aufgreifen der DataSource funktioniert nicht">
|
||||
<arrowlink COLOR="#ee185e" DESTINATION="ID_1509991147" ENDARROW="Default" ENDINCLINATION="-1069;58;" ID="Arrow_ID_1743534857" STARTARROW="None" STARTINCLINATION="-243;-15;"/>
|
||||
<icon BUILTIN="broken-line"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1710793091784" ID="ID_1970381008" MODIFIED="1710811906348" TEXT="TextTemplate_test">
|
||||
<icon BUILTIN="pencil"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1710793091784" ID="ID_1970381008" MODIFIED="1711589172965" TEXT="TextTemplate_test">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1710793387393" ID="ID_413372443" MODIFIED="1711322713365" TEXT="simpeUsage">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
|
|
@ -114085,13 +114218,15 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710793547451" ID="ID_778488140" MODIFIED="1711560211460" TEXT="verify_ETD_binding">
|
||||
<node COLOR="#338800" CREATED="1710793547451" ID="ID_778488140" MODIFIED="1711589168236" TEXT="verify_ETD_binding">
|
||||
<linktarget COLOR="#236cc2" DESTINATION="ID_778488140" ENDARROW="Default" ENDINCLINATION="97;-12;" ID="Arrow_ID_1117346354" SOURCE="ID_681566820" STARTARROW="None" STARTINCLINATION="67;272;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711560265587" ID="ID_1134491764" MODIFIED="1711560277302" TEXT="alles völlig analog wie beim Map-Binding">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1711560265587" ID="ID_1134491764" MODIFIED="1711589170215" TEXT="alles völlig analog wie beim Map-Binding">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1711560243235" ID="ID_415632339" MODIFIED="1711589171695" TEXT="ein Template damit instantieren">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node CREATED="1711560243235" ID="ID_415632339" MODIFIED="1711560257453" TEXT="ein Template damit instantieren"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue