diff options
| author | Scott Linder <scott@scottlinder.com> | 2018-11-14 19:39:59 +0000 |
|---|---|---|
| committer | Scott Linder <scott@scottlinder.com> | 2018-11-14 19:39:59 +0000 |
| commit | c0830f55779adf07809f6b1af4351c3a2f293dfc (patch) | |
| tree | 2e0903f2f91d399847025dea59473c66baf26b9d /llvm/include | |
| parent | 32dc5b9bf134b5a98c1a22395a5cd0ca5c409fe7 (diff) | |
| download | bcm5719-llvm-c0830f55779adf07809f6b1af4351c3a2f293dfc.tar.gz bcm5719-llvm-c0830f55779adf07809f6b1af4351c3a2f293dfc.zip | |
[Support] Teach YAMLIO about polymorphic types
Add support for "polymorphic" types to YAMLIO.
PolymorphicTraits can dynamically switch between other traits (Scalar, Map, or
Sequence). When inputting, the PolymorphicTraits type is told which type to
become, and when outputting the PolymorphicTraits type is asked which type it
currently is.
Also add support for TaggedScalarTraits to allow dynamically differentiating
between multiple scalar types using YAML tags.
Serialize empty maps as "{}" and empty sequences as "[]", so that types
are preserved when round-tripping PolymorphicTraits. This change has
equivalent semantics, but may break e.g. tests which compare output
verbatim.
Differential Revision: https://reviews.llvm.org/D48144
llvm-svn: 346884
Diffstat (limited to 'llvm/include')
| -rw-r--r-- | llvm/include/llvm/Support/YAMLTraits.h | 198 |
1 files changed, 191 insertions, 7 deletions
diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h index 6219755e83a..3d790e96fff 100644 --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -39,6 +39,12 @@ namespace llvm { namespace yaml { +enum class NodeKind : uint8_t { + Scalar, + Map, + Sequence, +}; + struct EmptyContext {}; /// This class should be specialized by any type that needs to be converted @@ -145,14 +151,14 @@ struct ScalarTraits { // Must provide: // // Function to write the value as a string: - //static void output(const T &value, void *ctxt, llvm::raw_ostream &out); + // static void output(const T &value, void *ctxt, llvm::raw_ostream &out); // // Function to convert a string to a value. Returns the empty // StringRef on success or an error string if string is malformed: - //static StringRef input(StringRef scalar, void *ctxt, T &value); + // static StringRef input(StringRef scalar, void *ctxt, T &value); // // Function to determine if the value should be quoted. - //static QuotingType mustQuote(StringRef); + // static QuotingType mustQuote(StringRef); }; /// This class should be specialized by type that requires custom conversion @@ -163,7 +169,7 @@ struct ScalarTraits { /// static void output(const MyType &Value, void*, llvm::raw_ostream &Out) /// { /// // stream out custom formatting -/// Out << Val; +/// Out << Value; /// } /// static StringRef input(StringRef Scalar, void*, MyType &Value) { /// // parse scalar and set `value` @@ -181,6 +187,47 @@ struct BlockScalarTraits { // Function to convert a string to a value. Returns the empty // StringRef on success or an error string if string is malformed: // static StringRef input(StringRef Scalar, void *ctxt, T &Value); + // + // Optional: + // static StringRef inputTag(T &Val, std::string Tag) + // static void outputTag(const T &Val, raw_ostream &Out) +}; + +/// This class should be specialized by type that requires custom conversion +/// to/from a YAML scalar with optional tags. For example: +/// +/// template <> +/// struct TaggedScalarTraits<MyType> { +/// static void output(const MyType &Value, void*, llvm::raw_ostream +/// &ScalarOut, llvm::raw_ostream &TagOut) +/// { +/// // stream out custom formatting including optional Tag +/// Out << Value; +/// } +/// static StringRef input(StringRef Scalar, StringRef Tag, void*, MyType +/// &Value) { +/// // parse scalar and set `value` +/// // return empty string on success, or error string +/// return StringRef(); +/// } +/// static QuotingType mustQuote(const MyType &Value, StringRef) { +/// return QuotingType::Single; +/// } +/// }; +template <typename T> struct TaggedScalarTraits { + // Must provide: + // + // Function to write the value and tag as strings: + // static void output(const T &Value, void *ctx, llvm::raw_ostream &ScalarOut, + // llvm::raw_ostream &TagOut); + // + // Function to convert a string to a value. Returns the empty + // StringRef on success or an error string if string is malformed: + // static StringRef input(StringRef Scalar, StringRef Tag, void *ctxt, T + // &Value); + // + // Function to determine if the value should be quoted. + // static QuotingType mustQuote(const T &Value, StringRef Scalar); }; /// This class should be specialized by any type that needs to be converted @@ -234,6 +281,31 @@ struct CustomMappingTraits { // static void output(IO &io, T &elem); }; +/// This class should be specialized by any type that can be represented as +/// a scalar, map, or sequence, decided dynamically. For example: +/// +/// typedef std::unique_ptr<MyBase> MyPoly; +/// +/// template<> +/// struct PolymorphicTraits<MyPoly> { +/// static NodeKind getKind(const MyPoly &poly) { +/// return poly->getKind(); +/// } +/// static MyScalar& getAsScalar(MyPoly &poly) { +/// if (!poly || !isa<MyScalar>(poly)) +/// poly.reset(new MyScalar()); +/// return *cast<MyScalar>(poly.get()); +/// } +/// // ... +/// }; +template <typename T> struct PolymorphicTraits { + // Must provide: + // static NodeKind getKind(const T &poly); + // static scalar_type &getAsScalar(T &poly); + // static map_type &getAsMap(T &poly); + // static sequence_type &getAsSequence(T &poly); +}; + // Only used for better diagnostics of missing traits template <typename T> struct MissingTrait; @@ -307,6 +379,24 @@ struct has_BlockScalarTraits (sizeof(test<BlockScalarTraits<T>>(nullptr, nullptr)) == 1); }; +// Test if TaggedScalarTraits<T> is defined on type T. +template <class T> struct has_TaggedScalarTraits { + using Signature_input = StringRef (*)(StringRef, StringRef, void *, T &); + using Signature_output = void (*)(const T &, void *, raw_ostream &, + raw_ostream &); + using Signature_mustQuote = QuotingType (*)(const T &, StringRef); + + template <typename U> + static char test(SameType<Signature_input, &U::input> *, + SameType<Signature_output, &U::output> *, + SameType<Signature_mustQuote, &U::mustQuote> *); + + template <typename U> static double test(...); + + static bool const value = + (sizeof(test<TaggedScalarTraits<T>>(nullptr, nullptr, nullptr)) == 1); +}; + // Test if MappingContextTraits<T> is defined on type T. template <class T, class Context> struct has_MappingTraits { using Signature_mapping = void (*)(class IO &, T &, Context &); @@ -438,6 +528,17 @@ struct has_DocumentListTraits static bool const value = (sizeof(test<DocumentListTraits<T>>(nullptr))==1); }; +template <class T> struct has_PolymorphicTraits { + using Signature_getKind = NodeKind (*)(const T &); + + template <typename U> + static char test(SameType<Signature_getKind, &U::getKind> *); + + template <typename U> static double test(...); + + static bool const value = (sizeof(test<PolymorphicTraits<T>>(nullptr)) == 1); +}; + inline bool isNumeric(StringRef S) { const static auto skipDigits = [](StringRef Input) { return Input.drop_front( @@ -626,10 +727,12 @@ struct missingTraits !has_ScalarBitSetTraits<T>::value && !has_ScalarTraits<T>::value && !has_BlockScalarTraits<T>::value && + !has_TaggedScalarTraits<T>::value && !has_MappingTraits<T, Context>::value && !has_SequenceTraits<T>::value && !has_CustomMappingTraits<T>::value && - !has_DocumentListTraits<T>::value> {}; + !has_DocumentListTraits<T>::value && + !has_PolymorphicTraits<T>::value> {}; template <typename T, typename Context> struct validatedMappingTraits @@ -683,6 +786,9 @@ public: virtual void scalarString(StringRef &, QuotingType) = 0; virtual void blockScalarString(StringRef &) = 0; + virtual void scalarTag(std::string &) = 0; + + virtual NodeKind getNodeKind() = 0; virtual void setError(const Twine &) = 0; @@ -917,6 +1023,31 @@ yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) { } } +template <typename T> +typename std::enable_if<has_TaggedScalarTraits<T>::value, void>::type +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { + if (io.outputting()) { + std::string ScalarStorage, TagStorage; + raw_string_ostream ScalarBuffer(ScalarStorage), TagBuffer(TagStorage); + TaggedScalarTraits<T>::output(Val, io.getContext(), ScalarBuffer, + TagBuffer); + io.scalarTag(TagBuffer.str()); + StringRef ScalarStr = ScalarBuffer.str(); + io.scalarString(ScalarStr, + TaggedScalarTraits<T>::mustQuote(Val, ScalarStr)); + } else { + std::string Tag; + io.scalarTag(Tag); + StringRef Str; + io.scalarString(Str, QuotingType::None); + StringRef Result = + TaggedScalarTraits<T>::input(Str, Tag, io.getContext(), Val); + if (!Result.empty()) { + io.setError(Twine(Result)); + } + } +} + template <typename T, typename Context> typename std::enable_if<validatedMappingTraits<T, Context>::value, void>::type yamlize(IO &io, T &Val, bool, Context &Ctx) { @@ -973,6 +1104,20 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { } template <typename T> +typename std::enable_if<has_PolymorphicTraits<T>::value, void>::type +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { + switch (io.outputting() ? PolymorphicTraits<T>::getKind(Val) + : io.getNodeKind()) { + case NodeKind::Scalar: + return yamlize(io, PolymorphicTraits<T>::getAsScalar(Val), true, Ctx); + case NodeKind::Map: + return yamlize(io, PolymorphicTraits<T>::getAsMap(Val), true, Ctx); + case NodeKind::Sequence: + return yamlize(io, PolymorphicTraits<T>::getAsSequence(Val), true, Ctx); + } +} + +template <typename T> typename std::enable_if<missingTraits<T, EmptyContext>::value, void>::type yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; @@ -1250,6 +1395,8 @@ private: void endBitSetScalar() override; void scalarString(StringRef &, QuotingType) override; void blockScalarString(StringRef &) override; + void scalarTag(std::string &) override; + NodeKind getNodeKind() override; void setError(const Twine &message) override; bool canElideEmptySequence() override; @@ -1395,6 +1542,8 @@ public: void endBitSetScalar() override; void scalarString(StringRef &, QuotingType) override; void blockScalarString(StringRef &) override; + void scalarTag(std::string &) override; + NodeKind getNodeKind() override; void setError(const Twine &message) override; bool canElideEmptySequence() override; @@ -1414,14 +1563,21 @@ private: void flowKey(StringRef Key); enum InState { - inSeq, - inFlowSeq, + inSeqFirstElement, + inSeqOtherElement, + inFlowSeqFirstElement, + inFlowSeqOtherElement, inMapFirstKey, inMapOtherKey, inFlowMapFirstKey, inFlowMapOtherKey }; + static bool inSeqAnyElement(InState State); + static bool inFlowSeqAnyElement(InState State); + static bool inMapAnyKey(InState State); + static bool inFlowMapAnyKey(InState State); + raw_ostream &Out; int WrapColumn; SmallVector<InState, 8> StateStack; @@ -1557,6 +1713,16 @@ operator>>(Input &In, T &Val) { return In; } +// Define non-member operator>> so that Input can stream in a polymorphic type. +template <typename T> +inline typename std::enable_if<has_PolymorphicTraits<T>::value, Input &>::type +operator>>(Input &In, T &Val) { + EmptyContext Ctx; + if (In.setCurrentDocument()) + yamlize(In, Val, true, Ctx); + return In; +} + // Provide better error message about types missing a trait specialization template <typename T> inline typename std::enable_if<missingTraits<T, EmptyContext>::value, @@ -1645,6 +1811,24 @@ operator<<(Output &Out, T &Val) { return Out; } +// Define non-member operator<< so that Output can stream out a polymorphic +// type. +template <typename T> +inline typename std::enable_if<has_PolymorphicTraits<T>::value, Output &>::type +operator<<(Output &Out, T &Val) { + EmptyContext Ctx; + Out.beginDocuments(); + if (Out.preflightDocument(0)) { + // FIXME: The parser does not support explicit documents terminated with a + // plain scalar; the end-marker is included as part of the scalar token. + assert(PolymorphicTraits<T>::getKind(Val) != NodeKind::Scalar && "plain scalar documents are not supported"); + yamlize(Out, Val, true, Ctx); + Out.postflightDocument(); + } + Out.endDocuments(); + return Out; +} + // Provide better error message about types missing a trait specialization template <typename T> inline typename std::enable_if<missingTraits<T, EmptyContext>::value, |

