Library: introduce compact textual representation for GenNode

This extension is required to use GenNode as data source for text-template instantiation.
I am aware that such a function could counter the design intent for GenNode,
because it could be (ab)used to "just get the damn value" and then
parse back the results...
This commit is contained in:
Fischlurch 2024-03-27 22:14:38 +01:00
parent 4c4ae0691c
commit cfe54a5070
5 changed files with 113 additions and 3 deletions

View file

@ -50,8 +50,10 @@
#include <boost/lexical_cast.hpp>
using util::join;
using boost::lexical_cast;
using lib::time::TimeValue;
using lib::transformIterator;
using util::almostEqual;
using lib::hash::LuidH;
@ -290,4 +292,70 @@ namespace diff{
}
/** compact textual representation of a Record<GenNode> (»object«). */
string renderCompact (Rec const& rec)
{
auto renderChild = [](diff::GenNode const& n){ return renderCompact(n); };
auto renderAttrib = [](diff::GenNode const& n){
return (n.isNamed()? n.idi.getSym()+"=" : "")
+ renderCompact(n);
};
return (Rec::TYPE_NIL==rec.getType()? "" : rec.getType())
+ "{"
+ join (transformIterator (rec.attribs(), renderAttrib))
+ (isnil(rec.scope())? "" : "|")
+ join (transformIterator (rec.scope() , renderChild))
+ "}"
;
}
string
renderCompact (RecRef const& ref)
{
return "Ref->" + (ref.empty()? util::BOTTOM_INDICATOR
: renderCompact (*ref.get()));
}
/** @remark presentation is oriented towards readability
* - numbers are slightly rounded (see \ref util::showDouble() )
* - time values are displayed timecode-like
* - nested scopes are displayed recursively, enclosed in curly brackets
* @see text-template-gen-node-binding.hpp
*/
string
renderCompact (GenNode const& node)
{
class Renderer
: public Variant<diff::DataValues>::Renderer
{
#define RENDER_CONTENT(_TY_) \
virtual string handle (_TY_ const& val) override { return util::toString(val); }
RENDER_CONTENT (int)
RENDER_CONTENT (int64_t)
RENDER_CONTENT (short)
RENDER_CONTENT (char)
RENDER_CONTENT (double)
RENDER_CONTENT (bool)
RENDER_CONTENT (time::Time)
RENDER_CONTENT (time::Offset)
RENDER_CONTENT (time::Duration)
RENDER_CONTENT (time::TimeSpan)
#undef RENDER_CONTENT
virtual string handle (string const& val) override { return val; }
virtual string handle (LuidH const& val) override { return util::showHash(val, 2);}
virtual string handle (RecRef const& ref) override { return renderCompact(ref); }
virtual string handle (Rec const& rec) override { return renderCompact(rec); }
};
Renderer visitor;
return node.data.accept (visitor);
}
}} // namespace lib::diff

View file

@ -501,6 +501,15 @@ namespace diff{
};
/** compact textual representation of a GenNode tree */
string renderCompact (GenNode const&);
string renderCompact (RecRef const&);
string renderCompact (Rec const&);
/**
* metafunction to detect types able to be wrapped into a GenNode.

View file

@ -683,6 +683,12 @@ namespace diff{
return record_;
}
operator string() const
{
return "Ref->" + (empty()? util::BOTTOM_INDICATOR
: string(*record_));
}
/** @note equality of references (instance pointers), not targets */
friend bool
operator== (RecordRef const& r1, RecordRef const& r2)

View file

@ -252,6 +252,13 @@ namespace lib {
virtual ~Predicate() { } ///< this is an interface
};
class Renderer
: public VisitorConstFunc<string>
{
public:
virtual ~Renderer() { } ///< this is an interface
}; ///////////////////////////////////TICKET #1361 : unable to make the Visitor fully generic
/**
* Metafunction to pick the first of the variant's types,
* which satisfies the given trait or predicate template
@ -274,9 +281,10 @@ namespace lib {
virtual ~Buffer() {} ///< this is an ABC with VTable
virtual void dispatch (Visitor&) =0;
virtual bool dispatch (Predicate&) const =0;
virtual operator string() const =0;
virtual void dispatch (Visitor&) =0;
virtual bool dispatch (Predicate&) const =0;
virtual string dispatch (Renderer&) const =0;
virtual operator string() const =0;
};
@ -379,6 +387,15 @@ namespace lib {
return typeDispatcher.handle (this->access());
}
string
dispatch (Renderer& visitor) const
{
using Dispatcher = variant::VFunc<string>::template ValueAcceptInterface<const TY>;
Dispatcher& typeDispatcher = visitor;
return typeDispatcher.handle (this->access());
}
/** diagnostic helper */
operator string() const;
};
@ -536,6 +553,12 @@ namespace lib {
{
return buffer().dispatch (visitor);
}
string
accept (Renderer& visitor) const
{
return buffer().dispatch (visitor);
}
};

View file

@ -147,6 +147,8 @@ namespace test{
CHECK (!n3.isNamed());
cout << n3 <<endl; // diagnostic spam
CHECK (renderCompact(n3) == "spam{ham=eggs}"_expect);
}
@ -289,6 +291,8 @@ namespace test{
for (auto & elm : n)
cout << elm <<endl;
CHECK (renderCompact(n) == "{hasSpam=true|*, ★, 3.1415927, ham{|eggs, spam, spam}, 0:00:00.000≺920ms≻, 42}"_expect);
auto iter = n.begin();
CHECK (!isnil (iter));