Library: supply a string-spec-binding for tests
...implemented by simply parsing the string into key=value pairs, which are then stored into a shared map. The actual data binding implementation can thus be inherited from the existing Map-binding
This commit is contained in:
parent
9b6fc3ebe5
commit
a89e272e35
3 changed files with 117 additions and 13 deletions
|
|
@ -106,7 +106,7 @@
|
|||
#include "lib/regex.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
|
|
@ -116,8 +116,6 @@
|
|||
namespace lib {
|
||||
namespace error = lumiera::error;
|
||||
|
||||
using std::optional;
|
||||
using std::nullopt;
|
||||
using std::string;
|
||||
using StrView = std::string_view;
|
||||
|
||||
|
|
@ -143,6 +141,17 @@ namespace lib {
|
|||
return explore (util::RegexSearchIter{iterDef, ACCEPT_DATA_ELM})
|
||||
.transform ([&](smatch mat){ return key+"."+string{mat[1]}+"."; });
|
||||
}
|
||||
|
||||
//-----------Syntax-for-key-value-data-from-string------
|
||||
const string MATCH_BINDING_TOK { R"~(([\w\.]+)\s*=\s*([^,;"\s]*)\s*)~"};
|
||||
const regex ACCEPT_BINDING_ELM {MATCH_DELIMITER + MATCH_BINDING_TOK};
|
||||
|
||||
inline auto
|
||||
iterBindingSeq (string const& dataDef)
|
||||
{
|
||||
return explore (util::RegexSearchIter{dataDef, ACCEPT_BINDING_ELM})
|
||||
.transform ([&](smatch mat){ return std::make_pair (string{mat[1]},string{mat[2]}); });
|
||||
}
|
||||
|
||||
|
||||
//-----------Syntax-for-TextTemplate-tags--------
|
||||
|
|
@ -320,12 +329,19 @@ namespace lib {
|
|||
|
||||
template<class DAT>
|
||||
InstanceIter<DAT>
|
||||
submit (DAT const& data) const;
|
||||
|
||||
template<class DAT>
|
||||
string
|
||||
render (DAT const& data) const;
|
||||
|
||||
template<class DAT>
|
||||
static string
|
||||
apply (string spec, DAT const& data);
|
||||
|
||||
auto keys() const;
|
||||
|
||||
/// @internal exposed for testing
|
||||
static ActionSeq compile (string const&);
|
||||
friend class test::TextTemplate_test;
|
||||
};
|
||||
|
|
@ -550,6 +566,10 @@ namespace lib {
|
|||
|
||||
bool isNested() { return not isnil (keyPrefix_); }
|
||||
|
||||
DataSource() = default;
|
||||
DataSource(MapS const& map)
|
||||
: data_{&map}
|
||||
{ }
|
||||
|
||||
bool
|
||||
contains (string key)
|
||||
|
|
@ -596,6 +616,31 @@ namespace lib {
|
|||
}
|
||||
};
|
||||
|
||||
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); });
|
||||
}
|
||||
|
||||
DataSource
|
||||
openContext (Iter& iter)
|
||||
{
|
||||
DataSource nested(*this);
|
||||
auto nestedBase = DataSource<MapS>::openContext (iter);
|
||||
nested.keyPrefix_ = nestedBase.keyPrefix_;
|
||||
return nested;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
@ -772,23 +817,40 @@ namespace lib {
|
|||
|
||||
/**
|
||||
* Instantiate this (pre-compiled) TextTemplate using the given data binding.
|
||||
* @return iterator to perform the evaluation and substitution step-by step,
|
||||
* thereby producing a sequence of `std::string_view&`
|
||||
*/
|
||||
template<class DAT>
|
||||
inline TextTemplate::InstanceIter<DAT>
|
||||
TextTemplate::render (DAT const& data) const
|
||||
TextTemplate::submit (DAT const& data) const
|
||||
{
|
||||
return explore (InstanceCore{actions_, DataSource<DAT>{&data}});
|
||||
return explore (InstanceCore{actions_, DataSource<DAT>{data}});
|
||||
}
|
||||
|
||||
/** */
|
||||
/** submit data and materialise rendered results into a single string */
|
||||
template<class DAT>
|
||||
inline string
|
||||
TextTemplate::render (DAT const& data) const
|
||||
{
|
||||
return util::join (submit (data), "");
|
||||
}
|
||||
|
||||
/** one-shot shorthand: compile a template and apply it to the given data */
|
||||
template<class DAT>
|
||||
inline string
|
||||
TextTemplate::apply (string spec, DAT const& data)
|
||||
{
|
||||
return util::join (TextTemplate(spec).render (data)
|
||||
,"");
|
||||
return TextTemplate(spec).render (data);
|
||||
}
|
||||
|
||||
/** diagnostics: query a list of all active keys expected by the template. */
|
||||
inline auto
|
||||
TextTemplate::keys() const
|
||||
{
|
||||
return explore (actions_)
|
||||
.filter ([](Action const& a){ return a.code == KEY or a.code == COND or a.code == ITER; })
|
||||
.transform([](Action const& a){ return a.val; });
|
||||
}
|
||||
|
||||
|
||||
}// namespace lib
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
using std::regex_search;
|
||||
using std::smatch;
|
||||
using util::_Fmt;
|
||||
using util::join;
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
|
@ -352,12 +353,20 @@ for} tail...
|
|||
}
|
||||
|
||||
|
||||
/** @test TODO
|
||||
* @todo WIP 4/24 🔁 define ⟶ implement
|
||||
|
||||
/** @test TODO Compile a template and instantiate with various data bindings.
|
||||
* @todo WIP 4/24 🔁 define ⟶ ✔ implement
|
||||
*/
|
||||
void
|
||||
verify_instantiation()
|
||||
{
|
||||
string wonder = "${a} / ${b} = (${a} + ${b})/${a} ≕ ${phi}";
|
||||
TextTemplate temple{wonder};
|
||||
CHECK (join(temple.keys()) == "a, b, a, b, a, phi"_expect);
|
||||
|
||||
auto insta = temple.submit (string{"phi=Φ, b=b, a=a"});
|
||||
CHECK (not isnil(insta));
|
||||
CHECK (join(insta,"⁐") == "⁐a⁐ / ⁐b⁐ = (⁐a⁐ + ⁐b⁐)/⁐a⁐ ≕ ⁐Φ⁐"_expect);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -113485,7 +113485,31 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710804833930" ID="ID_1766619477" MODIFIED="1710804839987" TEXT="Test-Binding">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<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"/>
|
||||
<node CREATED="1710804918648" ID="ID_1642910022" MODIFIED="1710804947480" TEXT="wird einmal geparst und in eine Map gepackt ⟶ Map-Binding">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711385576730" ID="ID_941884997" MODIFIED="1711385595496" TEXT="muß die geparste Map irgendwo dauerhaft bereithalten">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1711385600736" ID="ID_1043659791" MODIFIED="1711385609447" TEXT="packe sie in einen shared_Ptr">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1711385510203" ID="ID_1093619122" MODIFIED="1711385530084" TEXT="implementiert als Ableitung aus DataSource<MapS>">
|
||||
<node COLOR="#338800" CREATED="1711385567491" ID="ID_499165126" MODIFIED="1711385621632" TEXT="muß Konstruktor bereitstellen: parse und Map befüllen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1711385535918" ID="ID_379950106" MODIFIED="1711385566645" TEXT="muß für nested DataSrc eine Forwarding-Impl bereitstellen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1711385624332" ID="ID_1369205664" MODIFIED="1711385632315" TEXT="Probleme mit der Typ-Inferenz">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<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="#fafe99" COLOR="#fa002a" CREATED="1711385647409" ID="ID_61608130" MODIFIED="1711385658671" TEXT="er wird daher literal vom Argument-Typ abgegriffen">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710793367246" ID="ID_1695880616" MODIFIED="1710793369256" TEXT="ETD">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
|
|
@ -113519,8 +113543,17 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<node COLOR="#435e98" CREATED="1711322831209" ID="ID_1835045684" MODIFIED="1711322849605" TEXT="auch für leere Tags"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710793507744" ID="ID_1535977723" MODIFIED="1710793518661" TEXT="verify_instantiation">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1710793507744" ID="ID_1535977723" MODIFIED="1711385697214" TEXT="verify_instantiation">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1711385671550" ID="ID_723905669" MODIFIED="1711385695507" TEXT="erstes einfaches Beispiel mit reiner Substitution">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1711385747236" ID="ID_137823971" MODIFIED="1711385760162" TEXT="Diagnostik: alle aktiven Keys im compilierten Template">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1711385683468" ID="ID_1138359217" MODIFIED="1711385691931" TEXT="demonstriere den Iterator">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710793450480" ID="ID_703630650" MODIFIED="1710793457496" TEXT="verify_keySubstituton">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue