From 597d8191c715e55ebf23ed5537916ea512354217 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 27 Mar 2024 02:07:32 +0100 Subject: [PATCH] Library: code the DataSource template ...turns out challenging, since our intention here is borderline to the intended design of the Lumiera ETD. It ''should work'' though, when combined with a Variant-visitor... --- src/lib/diff/gen-node.cpp | 2 +- src/lib/test/test-helper.hpp | 2 +- src/lib/text-template-gen-node-binding.hpp | 183 +++++++++++++++++++++ src/lib/text-template.hpp | 1 + src/lib/util-quant.hpp | 1 + tests/library/text-template-test.cpp | 1 + wiki/thinkPad.ichthyo.mm | 180 +++++++++++++++++--- 7 files changed, 349 insertions(+), 21 deletions(-) create mode 100644 src/lib/text-template-gen-node-binding.hpp diff --git a/src/lib/diff/gen-node.cpp b/src/lib/diff/gen-node.cpp index 317a9234d..e3c7f28d6 100644 --- a/src/lib/diff/gen-node.cpp +++ b/src/lib/diff/gen-node.cpp @@ -157,7 +157,7 @@ namespace diff{ DataCap::matchDbl (double d) const { class MatchDouble - : public Variant::Predicate + : public Variant::Predicate //////////////////////////////////TICKET #1360 floating-point precision { double num_; diff --git a/src/lib/test/test-helper.hpp b/src/lib/test/test-helper.hpp index a2673c4ec..0a58c8fc0 100644 --- a/src/lib/test/test-helper.hpp +++ b/src/lib/test/test-helper.hpp @@ -75,7 +75,7 @@ namespace test{ { REQUIRE (0 < limit); return abs (val/target - F(1)) < limit; - } + } //////////////////////////////////////////////////////////////////////////TICKET #1360 looks like this problem was solved several times template diff --git a/src/lib/text-template-gen-node-binding.hpp b/src/lib/text-template-gen-node-binding.hpp new file mode 100644 index 000000000..8719b3bca --- /dev/null +++ b/src/lib/text-template-gen-node-binding.hpp @@ -0,0 +1,183 @@ +/* + TEXT-TEMPLATE-GEN-NODE-BINDING.hpp - data binding adapter for ETD + + Copyright (C) Lumiera.org + 2024, Hermann Vosseler + + 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 text-template-gen-node-binding.hpp + ** A complement to allow instantiation of a TextTemplate with ETD data. + ** Instead of requiring a specific data source type, the text template engine relies + ** on an extension point through a data-binding template, which can be (partially) specialised + ** to implement data access for any suitable kind of structured data. The Lumiera [ETD] is + ** a recursive data type comprised of lib::diff::GenNode elements, which in turn can hold + ** a small selection of binary data elements. This scheme for structured data is widely used + ** for internal communication between the components of the Lumiera application; it offers + ** all the structural elements to provide a full featured backing data structure for the + ** text template engine. + ** + ** # Intricacies + ** + ** Since the ETD is based on _binary data,_ we need to invoke a string rendering during + ** data access, in the end relying on util::toString(). This limits the precision of floating-pont + ** and time data (assuming this is adequate for the purpose of instantiating placeholders in a + ** text template). A further challenge arises from the openness of the ETD format, which is + ** intended for loose coupling between subsystems, and deliberately imposes not much structural + ** constraints, while also offering only very limited introspection capabilities (to prevent + ** a »programming by inspection« style). Some further conventions are thus necessary + ** - a _Scope_ is assumed to be a _Record-Node._ (»object structure«) + ** - _Keys_ are translated into _Attribute access._ + ** - _Iteration_ is assumed to pick a _loop-control Node_ and descend into this node's child scope. + ** - if such iterated children _happen to be simple values_, then a pseudo-scope is synthesised, + ** containing a single virtual attribute with the KeyID "value" (which implies that TextTemplate + ** 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 + */ + + +#ifndef LIB_TEXT_TEMPLATE_GEN_NODE_BINDING_H +#define LIB_TEXT_TEMPLATE_GEN_NODE_BINDING_H + + +#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 +#include +//#include +//#include +//#include + + +namespace lib { + + using std::string; +// using StrView = std::string_view; +// +// using util::_Fmt; +// using util::isnil; +// using util::unConst; + + + namespace { + + } + + /* ======= 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 }` tag. + * @see TextTemplate_test::verify_ETD_binding() + */ + template<> + struct TextTemplate::DataSource + { + 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(); + 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().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 + + inline auto + bindDataSource(diff::GenNode const& etd) + { + return TextTemplate::DataSource{etd}; + } + } + + + +}// namespace lib +#endif /*LIB_TEXT_TEMPLATE_GEN_NODE_BINDING_H*/ diff --git a/src/lib/text-template.hpp b/src/lib/text-template.hpp index 3856990e3..6b181f6a9 100644 --- a/src/lib/text-template.hpp +++ b/src/lib/text-template.hpp @@ -163,6 +163,7 @@ ** [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 ** @see SchedulerStress_test */ diff --git a/src/lib/util-quant.hpp b/src/lib/util-quant.hpp index 7da68ffd7..c1bded518 100644 --- a/src/lib/util-quant.hpp +++ b/src/lib/util-quant.hpp @@ -146,6 +146,7 @@ namespace util { * @see https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ * @see http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon * @see https://en.wikipedia.org/wiki/Unit_in_the_last_place + * @todo 3/2024 seems we have solved this problem several times meanwhile /////////////////////////////////TICKET #1360 sort out floating-point rounding and precision */ inline bool almostEqual (double d1, double d2, unsigned int ulp =2) diff --git a/tests/library/text-template-test.cpp b/tests/library/text-template-test.cpp index 115864b30..4b424f3c3 100644 --- a/tests/library/text-template-test.cpp +++ b/tests/library/text-template-test.cpp @@ -29,6 +29,7 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp"///////////////////////TODO #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 diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 70b629deb..666e3fff2 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -113640,8 +113640,8 @@ std::cout << tmpl.render({"what", "World"}) << s - - + + @@ -113698,7 +113698,8 @@ std::cout << tmpl.render({"what", "World"}) << s - + + @@ -113717,8 +113718,7 @@ std::cout << tmpl.render({"what", "World"}) << s Man könnte erwarten, daß diese string-view tatsächlich nur während der Iteration genutzt wird — letztlich möchte man ja das gerenderte Text-Template irgendwo integrieren...

- - + @@ -113736,16 +113736,68 @@ std::cout << tmpl.render({"what", "World"}) << s - - + + + + + + +

+ ...also auf Top-Level einen Record<GenNode> annehmen. Teilweise wird das im Diff-Framework und auch in diversen Tests so gehandhabt (da es einfacher ist) +

+ + +
+
+ + + + + + +

+ ...weil eine DataSource ein Referenz-Wrapper ist, und damit einen Pointer auf das Start-Element haben muß +

+ + +
+
- + + + + + + + + +

+ ....je länger ich darüber nachdenke — +

+

+ umso mehr sehe ich diese Idee im Widerspruch zum GenNode-Design +

+ + +
+ + + + + + + + + + + + @@ -113759,20 +113811,88 @@ std::cout << tmpl.render({"what", "World"}) << s Das ist gradezu die Grundannahme: der Verarbeiter hat die Struktur zu kennen — die Node hat keine introspektiven Fähigkeiten

- - +
- + + + + + + + + + + +

+ DataSrc-Impl kann sich auf stabile Memory-Location verlassen +

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

+ wir brauchen nur den Level traits + util + elementares Metaprogramming +

+ + +
+
+ + + + + +
- + + + + + + + + + + + + + + + + + @@ -113787,8 +113907,7 @@ std::cout << tmpl.render({"what", "World"}) << s ...denn in der Praxis wird erwartet, daß im Scope sehr wohl wieder verschachtelte Records liegen; sonst würde diese Datenstruktur ja wenig Sinn machen

- - +
@@ -113800,8 +113919,7 @@ std::cout << tmpl.render({"what", "World"}) << s ⟹ wenn Kind kein Record ist

- - + @@ -113818,15 +113936,39 @@ std::cout << tmpl.render({"what", "World"}) << s es gibt ein Prädikat genNode.isNested()

- - +
- + + + + + + + + + + + + + + + + + + + + + + + + + +