mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-24 03:33:20 +01:00
3df7bb7832
This is a short-term fix for PR33650 aimed to get the modules build bots green again. Remove all the places where we use the LLVM_YAML_IS_(FLOW_)?SEQUENCE_VECTOR macros to try to locally specialize a global template for a global type. That's not how C++ works. Instead, we now centrally define how to format vectors of fundamental types and of string (std::string and StringRef). We use flow formatting for the former cases, since that's the obvious right thing to do; in the latter case, it's less clear what the right choice is, but flow formatting is really bad for some cases (due to very long strings), so we pick block formatting. (Many of the cases that were using flow formatting for strings are improved by this change.) Other than the flow -> block formatting change for some vectors of strings, this should result in no functionality change. Differential Revision: https://reviews.llvm.org/D34907 Corresponding updates to clang, clang-tools-extra, and lld to follow. llvm-svn: 306878
1711 lines
55 KiB
C++
1711 lines
55 KiB
C++
//===- llvm/Support/YAMLTraits.h --------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_SUPPORT_YAMLTRAITS_H
|
|
#define LLVM_SUPPORT_YAMLTRAITS_H
|
|
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Support/AlignOf.h"
|
|
#include "llvm/Support/Allocator.h"
|
|
#include "llvm/Support/Endian.h"
|
|
#include "llvm/Support/Regex.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "llvm/Support/YAMLParser.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cassert>
|
|
#include <cctype>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <new>
|
|
#include <string>
|
|
#include <system_error>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
namespace llvm {
|
|
namespace yaml {
|
|
|
|
struct EmptyContext {};
|
|
|
|
/// This class should be specialized by any type that needs to be converted
|
|
/// to/from a YAML mapping. For example:
|
|
///
|
|
/// struct MappingTraits<MyStruct> {
|
|
/// static void mapping(IO &io, MyStruct &s) {
|
|
/// io.mapRequired("name", s.name);
|
|
/// io.mapRequired("size", s.size);
|
|
/// io.mapOptional("age", s.age);
|
|
/// }
|
|
/// };
|
|
template<class T>
|
|
struct MappingTraits {
|
|
// Must provide:
|
|
// static void mapping(IO &io, T &fields);
|
|
// Optionally may provide:
|
|
// static StringRef validate(IO &io, T &fields);
|
|
//
|
|
// The optional flow flag will cause generated YAML to use a flow mapping
|
|
// (e.g. { a: 0, b: 1 }):
|
|
// static const bool flow = true;
|
|
};
|
|
|
|
/// This class is similar to MappingTraits<T> but allows you to pass in
|
|
/// additional context for each map operation. For example:
|
|
///
|
|
/// struct MappingContextTraits<MyStruct, MyContext> {
|
|
/// static void mapping(IO &io, MyStruct &s, MyContext &c) {
|
|
/// io.mapRequired("name", s.name);
|
|
/// io.mapRequired("size", s.size);
|
|
/// io.mapOptional("age", s.age);
|
|
/// ++c.TimesMapped;
|
|
/// }
|
|
/// };
|
|
template <class T, class Context> struct MappingContextTraits {
|
|
// Must provide:
|
|
// static void mapping(IO &io, T &fields, Context &Ctx);
|
|
// Optionally may provide:
|
|
// static StringRef validate(IO &io, T &fields, Context &Ctx);
|
|
//
|
|
// The optional flow flag will cause generated YAML to use a flow mapping
|
|
// (e.g. { a: 0, b: 1 }):
|
|
// static const bool flow = true;
|
|
};
|
|
|
|
/// This class should be specialized by any integral type that converts
|
|
/// to/from a YAML scalar where there is a one-to-one mapping between
|
|
/// in-memory values and a string in YAML. For example:
|
|
///
|
|
/// struct ScalarEnumerationTraits<Colors> {
|
|
/// static void enumeration(IO &io, Colors &value) {
|
|
/// io.enumCase(value, "red", cRed);
|
|
/// io.enumCase(value, "blue", cBlue);
|
|
/// io.enumCase(value, "green", cGreen);
|
|
/// }
|
|
/// };
|
|
template<typename T>
|
|
struct ScalarEnumerationTraits {
|
|
// Must provide:
|
|
// static void enumeration(IO &io, T &value);
|
|
};
|
|
|
|
/// This class should be specialized by any integer type that is a union
|
|
/// of bit values and the YAML representation is a flow sequence of
|
|
/// strings. For example:
|
|
///
|
|
/// struct ScalarBitSetTraits<MyFlags> {
|
|
/// static void bitset(IO &io, MyFlags &value) {
|
|
/// io.bitSetCase(value, "big", flagBig);
|
|
/// io.bitSetCase(value, "flat", flagFlat);
|
|
/// io.bitSetCase(value, "round", flagRound);
|
|
/// }
|
|
/// };
|
|
template<typename T>
|
|
struct ScalarBitSetTraits {
|
|
// Must provide:
|
|
// static void bitset(IO &io, T &value);
|
|
};
|
|
|
|
/// This class should be specialized by type that requires custom conversion
|
|
/// to/from a yaml scalar. For example:
|
|
///
|
|
/// template<>
|
|
/// struct ScalarTraits<MyType> {
|
|
/// static void output(const MyType &val, void*, llvm::raw_ostream &out) {
|
|
/// // stream out custom formatting
|
|
/// out << llvm::format("%x", val);
|
|
/// }
|
|
/// static StringRef input(StringRef scalar, void*, MyType &value) {
|
|
/// // parse scalar and set `value`
|
|
/// // return empty string on success, or error string
|
|
/// return StringRef();
|
|
/// }
|
|
/// static bool mustQuote(StringRef) { return true; }
|
|
/// };
|
|
template<typename T>
|
|
struct ScalarTraits {
|
|
// Must provide:
|
|
//
|
|
// Function to write the value as a string:
|
|
//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);
|
|
//
|
|
// Function to determine if the value should be quoted.
|
|
//static bool mustQuote(StringRef);
|
|
};
|
|
|
|
/// This class should be specialized by type that requires custom conversion
|
|
/// to/from a YAML literal block scalar. For example:
|
|
///
|
|
/// template <>
|
|
/// struct BlockScalarTraits<MyType> {
|
|
/// static void output(const MyType &Value, void*, llvm::raw_ostream &Out)
|
|
/// {
|
|
/// // stream out custom formatting
|
|
/// Out << Val;
|
|
/// }
|
|
/// static StringRef input(StringRef Scalar, void*, MyType &Value) {
|
|
/// // parse scalar and set `value`
|
|
/// // return empty string on success, or error string
|
|
/// return StringRef();
|
|
/// }
|
|
/// };
|
|
template <typename T>
|
|
struct BlockScalarTraits {
|
|
// Must provide:
|
|
//
|
|
// Function to write the value as a string:
|
|
// static void output(const T &Value, void *ctx, 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);
|
|
};
|
|
|
|
/// This class should be specialized by any type that needs to be converted
|
|
/// to/from a YAML sequence. For example:
|
|
///
|
|
/// template<>
|
|
/// struct SequenceTraits<MyContainer> {
|
|
/// static size_t size(IO &io, MyContainer &seq) {
|
|
/// return seq.size();
|
|
/// }
|
|
/// static MyType& element(IO &, MyContainer &seq, size_t index) {
|
|
/// if ( index >= seq.size() )
|
|
/// seq.resize(index+1);
|
|
/// return seq[index];
|
|
/// }
|
|
/// };
|
|
template<typename T, typename EnableIf = void>
|
|
struct SequenceTraits {
|
|
// Must provide:
|
|
// static size_t size(IO &io, T &seq);
|
|
// static T::value_type& element(IO &io, T &seq, size_t index);
|
|
//
|
|
// The following is option and will cause generated YAML to use
|
|
// a flow sequence (e.g. [a,b,c]).
|
|
// static const bool flow = true;
|
|
};
|
|
|
|
/// This class should be specialized by any type for which vectors of that
|
|
/// type need to be converted to/from a YAML sequence.
|
|
template<typename T, typename EnableIf = void>
|
|
struct SequenceElementTraits {
|
|
// Must provide:
|
|
// static const bool flow;
|
|
};
|
|
|
|
/// This class should be specialized by any type that needs to be converted
|
|
/// to/from a list of YAML documents.
|
|
template<typename T>
|
|
struct DocumentListTraits {
|
|
// Must provide:
|
|
// static size_t size(IO &io, T &seq);
|
|
// static T::value_type& element(IO &io, T &seq, size_t index);
|
|
};
|
|
|
|
/// This class should be specialized by any type that needs to be converted
|
|
/// to/from a YAML mapping in the case where the names of the keys are not known
|
|
/// in advance, e.g. a string map.
|
|
template <typename T>
|
|
struct CustomMappingTraits {
|
|
// static void inputOne(IO &io, StringRef key, T &elem);
|
|
// static void output(IO &io, T &elem);
|
|
};
|
|
|
|
// Only used for better diagnostics of missing traits
|
|
template <typename T>
|
|
struct MissingTrait;
|
|
|
|
// Test if ScalarEnumerationTraits<T> is defined on type T.
|
|
template <class T>
|
|
struct has_ScalarEnumerationTraits
|
|
{
|
|
using Signature_enumeration = void (*)(class IO&, T&);
|
|
|
|
template <typename U>
|
|
static char test(SameType<Signature_enumeration, &U::enumeration>*);
|
|
|
|
template <typename U>
|
|
static double test(...);
|
|
|
|
public:
|
|
static bool const value =
|
|
(sizeof(test<ScalarEnumerationTraits<T>>(nullptr)) == 1);
|
|
};
|
|
|
|
// Test if ScalarBitSetTraits<T> is defined on type T.
|
|
template <class T>
|
|
struct has_ScalarBitSetTraits
|
|
{
|
|
using Signature_bitset = void (*)(class IO&, T&);
|
|
|
|
template <typename U>
|
|
static char test(SameType<Signature_bitset, &U::bitset>*);
|
|
|
|
template <typename U>
|
|
static double test(...);
|
|
|
|
public:
|
|
static bool const value = (sizeof(test<ScalarBitSetTraits<T>>(nullptr)) == 1);
|
|
};
|
|
|
|
// Test if ScalarTraits<T> is defined on type T.
|
|
template <class T>
|
|
struct has_ScalarTraits
|
|
{
|
|
using Signature_input = StringRef (*)(StringRef, void*, T&);
|
|
using Signature_output = void (*)(const T&, void*, raw_ostream&);
|
|
using Signature_mustQuote = bool (*)(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(...);
|
|
|
|
public:
|
|
static bool const value =
|
|
(sizeof(test<ScalarTraits<T>>(nullptr, nullptr, nullptr)) == 1);
|
|
};
|
|
|
|
// Test if BlockScalarTraits<T> is defined on type T.
|
|
template <class T>
|
|
struct has_BlockScalarTraits
|
|
{
|
|
using Signature_input = StringRef (*)(StringRef, void *, T &);
|
|
using Signature_output = void (*)(const T &, void *, raw_ostream &);
|
|
|
|
template <typename U>
|
|
static char test(SameType<Signature_input, &U::input> *,
|
|
SameType<Signature_output, &U::output> *);
|
|
|
|
template <typename U>
|
|
static double test(...);
|
|
|
|
public:
|
|
static bool const value =
|
|
(sizeof(test<BlockScalarTraits<T>>(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 &);
|
|
|
|
template <typename U>
|
|
static char test(SameType<Signature_mapping, &U::mapping>*);
|
|
|
|
template <typename U>
|
|
static double test(...);
|
|
|
|
public:
|
|
static bool const value =
|
|
(sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1);
|
|
};
|
|
|
|
// Test if MappingTraits<T> is defined on type T.
|
|
template <class T> struct has_MappingTraits<T, EmptyContext> {
|
|
using Signature_mapping = void (*)(class IO &, T &);
|
|
|
|
template <typename U>
|
|
static char test(SameType<Signature_mapping, &U::mapping> *);
|
|
|
|
template <typename U> static double test(...);
|
|
|
|
public:
|
|
static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1);
|
|
};
|
|
|
|
// Test if MappingContextTraits<T>::validate() is defined on type T.
|
|
template <class T, class Context> struct has_MappingValidateTraits {
|
|
using Signature_validate = StringRef (*)(class IO &, T &, Context &);
|
|
|
|
template <typename U>
|
|
static char test(SameType<Signature_validate, &U::validate>*);
|
|
|
|
template <typename U>
|
|
static double test(...);
|
|
|
|
public:
|
|
static bool const value =
|
|
(sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1);
|
|
};
|
|
|
|
// Test if MappingTraits<T>::validate() is defined on type T.
|
|
template <class T> struct has_MappingValidateTraits<T, EmptyContext> {
|
|
using Signature_validate = StringRef (*)(class IO &, T &);
|
|
|
|
template <typename U>
|
|
static char test(SameType<Signature_validate, &U::validate> *);
|
|
|
|
template <typename U> static double test(...);
|
|
|
|
public:
|
|
static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1);
|
|
};
|
|
|
|
// Test if SequenceTraits<T> is defined on type T.
|
|
template <class T>
|
|
struct has_SequenceMethodTraits
|
|
{
|
|
using Signature_size = size_t (*)(class IO&, T&);
|
|
|
|
template <typename U>
|
|
static char test(SameType<Signature_size, &U::size>*);
|
|
|
|
template <typename U>
|
|
static double test(...);
|
|
|
|
public:
|
|
static bool const value = (sizeof(test<SequenceTraits<T>>(nullptr)) == 1);
|
|
};
|
|
|
|
// Test if CustomMappingTraits<T> is defined on type T.
|
|
template <class T>
|
|
struct has_CustomMappingTraits
|
|
{
|
|
using Signature_input = void (*)(IO &io, StringRef key, T &v);
|
|
|
|
template <typename U>
|
|
static char test(SameType<Signature_input, &U::inputOne>*);
|
|
|
|
template <typename U>
|
|
static double test(...);
|
|
|
|
public:
|
|
static bool const value =
|
|
(sizeof(test<CustomMappingTraits<T>>(nullptr)) == 1);
|
|
};
|
|
|
|
// has_FlowTraits<int> will cause an error with some compilers because
|
|
// it subclasses int. Using this wrapper only instantiates the
|
|
// real has_FlowTraits only if the template type is a class.
|
|
template <typename T, bool Enabled = std::is_class<T>::value>
|
|
class has_FlowTraits
|
|
{
|
|
public:
|
|
static const bool value = false;
|
|
};
|
|
|
|
// Some older gcc compilers don't support straight forward tests
|
|
// for members, so test for ambiguity cause by the base and derived
|
|
// classes both defining the member.
|
|
template <class T>
|
|
struct has_FlowTraits<T, true>
|
|
{
|
|
struct Fallback { bool flow; };
|
|
struct Derived : T, Fallback { };
|
|
|
|
template<typename C>
|
|
static char (&f(SameType<bool Fallback::*, &C::flow>*))[1];
|
|
|
|
template<typename C>
|
|
static char (&f(...))[2];
|
|
|
|
public:
|
|
static bool const value = sizeof(f<Derived>(nullptr)) == 2;
|
|
};
|
|
|
|
// Test if SequenceTraits<T> is defined on type T
|
|
template<typename T>
|
|
struct has_SequenceTraits : public std::integral_constant<bool,
|
|
has_SequenceMethodTraits<T>::value > { };
|
|
|
|
// Test if DocumentListTraits<T> is defined on type T
|
|
template <class T>
|
|
struct has_DocumentListTraits
|
|
{
|
|
using Signature_size = size_t (*)(class IO &, T &);
|
|
|
|
template <typename U>
|
|
static char test(SameType<Signature_size, &U::size>*);
|
|
|
|
template <typename U>
|
|
static double test(...);
|
|
|
|
public:
|
|
static bool const value = (sizeof(test<DocumentListTraits<T>>(nullptr))==1);
|
|
};
|
|
|
|
inline bool isNumber(StringRef S) {
|
|
static const char OctalChars[] = "01234567";
|
|
if (S.startswith("0") &&
|
|
S.drop_front().find_first_not_of(OctalChars) == StringRef::npos)
|
|
return true;
|
|
|
|
if (S.startswith("0o") &&
|
|
S.drop_front(2).find_first_not_of(OctalChars) == StringRef::npos)
|
|
return true;
|
|
|
|
static const char HexChars[] = "0123456789abcdefABCDEF";
|
|
if (S.startswith("0x") &&
|
|
S.drop_front(2).find_first_not_of(HexChars) == StringRef::npos)
|
|
return true;
|
|
|
|
static const char DecChars[] = "0123456789";
|
|
if (S.find_first_not_of(DecChars) == StringRef::npos)
|
|
return true;
|
|
|
|
if (S.equals(".inf") || S.equals(".Inf") || S.equals(".INF"))
|
|
return true;
|
|
|
|
Regex FloatMatcher("^(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$");
|
|
if (FloatMatcher.match(S))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
inline bool isNumeric(StringRef S) {
|
|
if ((S.front() == '-' || S.front() == '+') && isNumber(S.drop_front()))
|
|
return true;
|
|
|
|
if (isNumber(S))
|
|
return true;
|
|
|
|
if (S.equals(".nan") || S.equals(".NaN") || S.equals(".NAN"))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
inline bool isNull(StringRef S) {
|
|
return S.equals("null") || S.equals("Null") || S.equals("NULL") ||
|
|
S.equals("~");
|
|
}
|
|
|
|
inline bool isBool(StringRef S) {
|
|
return S.equals("true") || S.equals("True") || S.equals("TRUE") ||
|
|
S.equals("false") || S.equals("False") || S.equals("FALSE");
|
|
}
|
|
|
|
inline bool needsQuotes(StringRef S) {
|
|
if (S.empty())
|
|
return true;
|
|
if (isspace(S.front()) || isspace(S.back()))
|
|
return true;
|
|
if (S.front() == ',')
|
|
return true;
|
|
|
|
static const char ScalarSafeChars[] =
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-/^., \t";
|
|
if (S.find_first_not_of(ScalarSafeChars) != StringRef::npos)
|
|
return true;
|
|
|
|
if (isNull(S))
|
|
return true;
|
|
if (isBool(S))
|
|
return true;
|
|
if (isNumeric(S))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
struct missingTraits
|
|
: public std::integral_constant<bool,
|
|
!has_ScalarEnumerationTraits<T>::value &&
|
|
!has_ScalarBitSetTraits<T>::value &&
|
|
!has_ScalarTraits<T>::value &&
|
|
!has_BlockScalarTraits<T>::value &&
|
|
!has_MappingTraits<T, Context>::value &&
|
|
!has_SequenceTraits<T>::value &&
|
|
!has_CustomMappingTraits<T>::value &&
|
|
!has_DocumentListTraits<T>::value> {};
|
|
|
|
template <typename T, typename Context>
|
|
struct validatedMappingTraits
|
|
: public std::integral_constant<
|
|
bool, has_MappingTraits<T, Context>::value &&
|
|
has_MappingValidateTraits<T, Context>::value> {};
|
|
|
|
template <typename T, typename Context>
|
|
struct unvalidatedMappingTraits
|
|
: public std::integral_constant<
|
|
bool, has_MappingTraits<T, Context>::value &&
|
|
!has_MappingValidateTraits<T, Context>::value> {};
|
|
|
|
// Base class for Input and Output.
|
|
class IO {
|
|
public:
|
|
IO(void *Ctxt = nullptr);
|
|
virtual ~IO();
|
|
|
|
virtual bool outputting() = 0;
|
|
|
|
virtual unsigned beginSequence() = 0;
|
|
virtual bool preflightElement(unsigned, void *&) = 0;
|
|
virtual void postflightElement(void*) = 0;
|
|
virtual void endSequence() = 0;
|
|
virtual bool canElideEmptySequence() = 0;
|
|
|
|
virtual unsigned beginFlowSequence() = 0;
|
|
virtual bool preflightFlowElement(unsigned, void *&) = 0;
|
|
virtual void postflightFlowElement(void*) = 0;
|
|
virtual void endFlowSequence() = 0;
|
|
|
|
virtual bool mapTag(StringRef Tag, bool Default=false) = 0;
|
|
virtual void beginMapping() = 0;
|
|
virtual void endMapping() = 0;
|
|
virtual bool preflightKey(const char*, bool, bool, bool &, void *&) = 0;
|
|
virtual void postflightKey(void*) = 0;
|
|
virtual std::vector<StringRef> keys() = 0;
|
|
|
|
virtual void beginFlowMapping() = 0;
|
|
virtual void endFlowMapping() = 0;
|
|
|
|
virtual void beginEnumScalar() = 0;
|
|
virtual bool matchEnumScalar(const char*, bool) = 0;
|
|
virtual bool matchEnumFallback() = 0;
|
|
virtual void endEnumScalar() = 0;
|
|
|
|
virtual bool beginBitSetScalar(bool &) = 0;
|
|
virtual bool bitSetMatch(const char*, bool) = 0;
|
|
virtual void endBitSetScalar() = 0;
|
|
|
|
virtual void scalarString(StringRef &, bool) = 0;
|
|
virtual void blockScalarString(StringRef &) = 0;
|
|
|
|
virtual void setError(const Twine &) = 0;
|
|
|
|
template <typename T>
|
|
void enumCase(T &Val, const char* Str, const T ConstVal) {
|
|
if ( matchEnumScalar(Str, outputting() && Val == ConstVal) ) {
|
|
Val = ConstVal;
|
|
}
|
|
}
|
|
|
|
// allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF
|
|
template <typename T>
|
|
void enumCase(T &Val, const char* Str, const uint32_t ConstVal) {
|
|
if ( matchEnumScalar(Str, outputting() && Val == static_cast<T>(ConstVal)) ) {
|
|
Val = ConstVal;
|
|
}
|
|
}
|
|
|
|
template <typename FBT, typename T>
|
|
void enumFallback(T &Val) {
|
|
if (matchEnumFallback()) {
|
|
EmptyContext Context;
|
|
// FIXME: Force integral conversion to allow strong typedefs to convert.
|
|
FBT Res = static_cast<typename FBT::BaseType>(Val);
|
|
yamlize(*this, Res, true, Context);
|
|
Val = static_cast<T>(static_cast<typename FBT::BaseType>(Res));
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void bitSetCase(T &Val, const char* Str, const T ConstVal) {
|
|
if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) {
|
|
Val = static_cast<T>(Val | ConstVal);
|
|
}
|
|
}
|
|
|
|
// allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF
|
|
template <typename T>
|
|
void bitSetCase(T &Val, const char* Str, const uint32_t ConstVal) {
|
|
if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) {
|
|
Val = static_cast<T>(Val | ConstVal);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void maskedBitSetCase(T &Val, const char *Str, T ConstVal, T Mask) {
|
|
if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal))
|
|
Val = Val | ConstVal;
|
|
}
|
|
|
|
template <typename T>
|
|
void maskedBitSetCase(T &Val, const char *Str, uint32_t ConstVal,
|
|
uint32_t Mask) {
|
|
if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal))
|
|
Val = Val | ConstVal;
|
|
}
|
|
|
|
void *getContext();
|
|
void setContext(void *);
|
|
|
|
template <typename T> void mapRequired(const char *Key, T &Val) {
|
|
EmptyContext Ctx;
|
|
this->processKey(Key, Val, true, Ctx);
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
void mapRequired(const char *Key, T &Val, Context &Ctx) {
|
|
this->processKey(Key, Val, true, Ctx);
|
|
}
|
|
|
|
template <typename T> void mapOptional(const char *Key, T &Val) {
|
|
EmptyContext Ctx;
|
|
mapOptionalWithContext(Key, Val, Ctx);
|
|
}
|
|
|
|
template <typename T>
|
|
void mapOptional(const char *Key, T &Val, const T &Default) {
|
|
EmptyContext Ctx;
|
|
mapOptionalWithContext(Key, Val, Default, Ctx);
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
typename std::enable_if<has_SequenceTraits<T>::value, void>::type
|
|
mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) {
|
|
// omit key/value instead of outputting empty sequence
|
|
if (this->canElideEmptySequence() && !(Val.begin() != Val.end()))
|
|
return;
|
|
this->processKey(Key, Val, false, Ctx);
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
void mapOptionalWithContext(const char *Key, Optional<T> &Val, Context &Ctx) {
|
|
this->processKeyWithDefault(Key, Val, Optional<T>(), /*Required=*/false,
|
|
Ctx);
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
typename std::enable_if<!has_SequenceTraits<T>::value, void>::type
|
|
mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) {
|
|
this->processKey(Key, Val, false, Ctx);
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
void mapOptionalWithContext(const char *Key, T &Val, const T &Default,
|
|
Context &Ctx) {
|
|
this->processKeyWithDefault(Key, Val, Default, false, Ctx);
|
|
}
|
|
|
|
private:
|
|
template <typename T, typename Context>
|
|
void processKeyWithDefault(const char *Key, Optional<T> &Val,
|
|
const Optional<T> &DefaultValue, bool Required,
|
|
Context &Ctx) {
|
|
assert(DefaultValue.hasValue() == false &&
|
|
"Optional<T> shouldn't have a value!");
|
|
void *SaveInfo;
|
|
bool UseDefault = true;
|
|
const bool sameAsDefault = outputting() && !Val.hasValue();
|
|
if (!outputting() && !Val.hasValue())
|
|
Val = T();
|
|
if (Val.hasValue() &&
|
|
this->preflightKey(Key, Required, sameAsDefault, UseDefault,
|
|
SaveInfo)) {
|
|
yamlize(*this, Val.getValue(), Required, Ctx);
|
|
this->postflightKey(SaveInfo);
|
|
} else {
|
|
if (UseDefault)
|
|
Val = DefaultValue;
|
|
}
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
void processKeyWithDefault(const char *Key, T &Val, const T &DefaultValue,
|
|
bool Required, Context &Ctx) {
|
|
void *SaveInfo;
|
|
bool UseDefault;
|
|
const bool sameAsDefault = outputting() && Val == DefaultValue;
|
|
if ( this->preflightKey(Key, Required, sameAsDefault, UseDefault,
|
|
SaveInfo) ) {
|
|
yamlize(*this, Val, Required, Ctx);
|
|
this->postflightKey(SaveInfo);
|
|
}
|
|
else {
|
|
if ( UseDefault )
|
|
Val = DefaultValue;
|
|
}
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
void processKey(const char *Key, T &Val, bool Required, Context &Ctx) {
|
|
void *SaveInfo;
|
|
bool UseDefault;
|
|
if ( this->preflightKey(Key, Required, false, UseDefault, SaveInfo) ) {
|
|
yamlize(*this, Val, Required, Ctx);
|
|
this->postflightKey(SaveInfo);
|
|
}
|
|
}
|
|
|
|
private:
|
|
void *Ctxt;
|
|
};
|
|
|
|
namespace detail {
|
|
|
|
template <typename T, typename Context>
|
|
void doMapping(IO &io, T &Val, Context &Ctx) {
|
|
MappingContextTraits<T, Context>::mapping(io, Val, Ctx);
|
|
}
|
|
|
|
template <typename T> void doMapping(IO &io, T &Val, EmptyContext &Ctx) {
|
|
MappingTraits<T>::mapping(io, Val);
|
|
}
|
|
|
|
} // end namespace detail
|
|
|
|
template <typename T>
|
|
typename std::enable_if<has_ScalarEnumerationTraits<T>::value, void>::type
|
|
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
|
|
io.beginEnumScalar();
|
|
ScalarEnumerationTraits<T>::enumeration(io, Val);
|
|
io.endEnumScalar();
|
|
}
|
|
|
|
template <typename T>
|
|
typename std::enable_if<has_ScalarBitSetTraits<T>::value, void>::type
|
|
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
|
|
bool DoClear;
|
|
if ( io.beginBitSetScalar(DoClear) ) {
|
|
if ( DoClear )
|
|
Val = static_cast<T>(0);
|
|
ScalarBitSetTraits<T>::bitset(io, Val);
|
|
io.endBitSetScalar();
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
typename std::enable_if<has_ScalarTraits<T>::value, void>::type
|
|
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
|
|
if ( io.outputting() ) {
|
|
std::string Storage;
|
|
raw_string_ostream Buffer(Storage);
|
|
ScalarTraits<T>::output(Val, io.getContext(), Buffer);
|
|
StringRef Str = Buffer.str();
|
|
io.scalarString(Str, ScalarTraits<T>::mustQuote(Str));
|
|
}
|
|
else {
|
|
StringRef Str;
|
|
io.scalarString(Str, ScalarTraits<T>::mustQuote(Str));
|
|
StringRef Result = ScalarTraits<T>::input(Str, io.getContext(), Val);
|
|
if ( !Result.empty() ) {
|
|
io.setError(Twine(Result));
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
typename std::enable_if<has_BlockScalarTraits<T>::value, void>::type
|
|
yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) {
|
|
if (YamlIO.outputting()) {
|
|
std::string Storage;
|
|
raw_string_ostream Buffer(Storage);
|
|
BlockScalarTraits<T>::output(Val, YamlIO.getContext(), Buffer);
|
|
StringRef Str = Buffer.str();
|
|
YamlIO.blockScalarString(Str);
|
|
} else {
|
|
StringRef Str;
|
|
YamlIO.blockScalarString(Str);
|
|
StringRef Result =
|
|
BlockScalarTraits<T>::input(Str, YamlIO.getContext(), Val);
|
|
if (!Result.empty())
|
|
YamlIO.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) {
|
|
if (has_FlowTraits<MappingTraits<T>>::value)
|
|
io.beginFlowMapping();
|
|
else
|
|
io.beginMapping();
|
|
if (io.outputting()) {
|
|
StringRef Err = MappingTraits<T>::validate(io, Val);
|
|
if (!Err.empty()) {
|
|
errs() << Err << "\n";
|
|
assert(Err.empty() && "invalid struct trying to be written as yaml");
|
|
}
|
|
}
|
|
detail::doMapping(io, Val, Ctx);
|
|
if (!io.outputting()) {
|
|
StringRef Err = MappingTraits<T>::validate(io, Val);
|
|
if (!Err.empty())
|
|
io.setError(Err);
|
|
}
|
|
if (has_FlowTraits<MappingTraits<T>>::value)
|
|
io.endFlowMapping();
|
|
else
|
|
io.endMapping();
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
typename std::enable_if<unvalidatedMappingTraits<T, Context>::value, void>::type
|
|
yamlize(IO &io, T &Val, bool, Context &Ctx) {
|
|
if (has_FlowTraits<MappingTraits<T>>::value) {
|
|
io.beginFlowMapping();
|
|
detail::doMapping(io, Val, Ctx);
|
|
io.endFlowMapping();
|
|
} else {
|
|
io.beginMapping();
|
|
detail::doMapping(io, Val, Ctx);
|
|
io.endMapping();
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
typename std::enable_if<has_CustomMappingTraits<T>::value, void>::type
|
|
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
|
|
if ( io.outputting() ) {
|
|
io.beginMapping();
|
|
CustomMappingTraits<T>::output(io, Val);
|
|
io.endMapping();
|
|
} else {
|
|
io.beginMapping();
|
|
for (StringRef key : io.keys())
|
|
CustomMappingTraits<T>::inputOne(io, key, Val);
|
|
io.endMapping();
|
|
}
|
|
}
|
|
|
|
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>)];
|
|
}
|
|
|
|
template <typename T, typename Context>
|
|
typename std::enable_if<has_SequenceTraits<T>::value, void>::type
|
|
yamlize(IO &io, T &Seq, bool, Context &Ctx) {
|
|
if ( has_FlowTraits< SequenceTraits<T>>::value ) {
|
|
unsigned incnt = io.beginFlowSequence();
|
|
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
|
|
for(unsigned i=0; i < count; ++i) {
|
|
void *SaveInfo;
|
|
if ( io.preflightFlowElement(i, SaveInfo) ) {
|
|
yamlize(io, SequenceTraits<T>::element(io, Seq, i), true, Ctx);
|
|
io.postflightFlowElement(SaveInfo);
|
|
}
|
|
}
|
|
io.endFlowSequence();
|
|
}
|
|
else {
|
|
unsigned incnt = io.beginSequence();
|
|
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
|
|
for(unsigned i=0; i < count; ++i) {
|
|
void *SaveInfo;
|
|
if ( io.preflightElement(i, SaveInfo) ) {
|
|
yamlize(io, SequenceTraits<T>::element(io, Seq, i), true, Ctx);
|
|
io.postflightElement(SaveInfo);
|
|
}
|
|
}
|
|
io.endSequence();
|
|
}
|
|
}
|
|
|
|
template<>
|
|
struct ScalarTraits<bool> {
|
|
static void output(const bool &, void* , raw_ostream &);
|
|
static StringRef input(StringRef, void *, bool &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<StringRef> {
|
|
static void output(const StringRef &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, StringRef &);
|
|
static bool mustQuote(StringRef S) { return needsQuotes(S); }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<std::string> {
|
|
static void output(const std::string &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, std::string &);
|
|
static bool mustQuote(StringRef S) { return needsQuotes(S); }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<uint8_t> {
|
|
static void output(const uint8_t &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, uint8_t &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<uint16_t> {
|
|
static void output(const uint16_t &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, uint16_t &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<uint32_t> {
|
|
static void output(const uint32_t &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, uint32_t &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<uint64_t> {
|
|
static void output(const uint64_t &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, uint64_t &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<int8_t> {
|
|
static void output(const int8_t &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, int8_t &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<int16_t> {
|
|
static void output(const int16_t &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, int16_t &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<int32_t> {
|
|
static void output(const int32_t &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, int32_t &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<int64_t> {
|
|
static void output(const int64_t &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, int64_t &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<float> {
|
|
static void output(const float &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, float &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<double> {
|
|
static void output(const double &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, double &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
// For endian types, we just use the existing ScalarTraits for the underlying
|
|
// type. This way endian aware types are supported whenever a ScalarTraits
|
|
// is defined for the underlying type.
|
|
template <typename value_type, support::endianness endian, size_t alignment>
|
|
struct ScalarTraits<support::detail::packed_endian_specific_integral<
|
|
value_type, endian, alignment>> {
|
|
using endian_type =
|
|
support::detail::packed_endian_specific_integral<value_type, endian,
|
|
alignment>;
|
|
|
|
static void output(const endian_type &E, void *Ctx, raw_ostream &Stream) {
|
|
ScalarTraits<value_type>::output(static_cast<value_type>(E), Ctx, Stream);
|
|
}
|
|
|
|
static StringRef input(StringRef Str, void *Ctx, endian_type &E) {
|
|
value_type V;
|
|
auto R = ScalarTraits<value_type>::input(Str, Ctx, V);
|
|
E = static_cast<endian_type>(V);
|
|
return R;
|
|
}
|
|
|
|
static bool mustQuote(StringRef Str) {
|
|
return ScalarTraits<value_type>::mustQuote(Str);
|
|
}
|
|
};
|
|
|
|
// Utility for use within MappingTraits<>::mapping() method
|
|
// to [de]normalize an object for use with YAML conversion.
|
|
template <typename TNorm, typename TFinal>
|
|
struct MappingNormalization {
|
|
MappingNormalization(IO &i_o, TFinal &Obj)
|
|
: io(i_o), BufPtr(nullptr), Result(Obj) {
|
|
if ( io.outputting() ) {
|
|
BufPtr = new (&Buffer) TNorm(io, Obj);
|
|
}
|
|
else {
|
|
BufPtr = new (&Buffer) TNorm(io);
|
|
}
|
|
}
|
|
|
|
~MappingNormalization() {
|
|
if ( ! io.outputting() ) {
|
|
Result = BufPtr->denormalize(io);
|
|
}
|
|
BufPtr->~TNorm();
|
|
}
|
|
|
|
TNorm* operator->() { return BufPtr; }
|
|
|
|
private:
|
|
using Storage = AlignedCharArrayUnion<TNorm>;
|
|
|
|
Storage Buffer;
|
|
IO &io;
|
|
TNorm *BufPtr;
|
|
TFinal &Result;
|
|
};
|
|
|
|
// Utility for use within MappingTraits<>::mapping() method
|
|
// to [de]normalize an object for use with YAML conversion.
|
|
template <typename TNorm, typename TFinal>
|
|
struct MappingNormalizationHeap {
|
|
MappingNormalizationHeap(IO &i_o, TFinal &Obj, BumpPtrAllocator *allocator)
|
|
: io(i_o), Result(Obj) {
|
|
if ( io.outputting() ) {
|
|
BufPtr = new (&Buffer) TNorm(io, Obj);
|
|
}
|
|
else if (allocator) {
|
|
BufPtr = allocator->Allocate<TNorm>();
|
|
new (BufPtr) TNorm(io);
|
|
} else {
|
|
BufPtr = new TNorm(io);
|
|
}
|
|
}
|
|
|
|
~MappingNormalizationHeap() {
|
|
if ( io.outputting() ) {
|
|
BufPtr->~TNorm();
|
|
}
|
|
else {
|
|
Result = BufPtr->denormalize(io);
|
|
}
|
|
}
|
|
|
|
TNorm* operator->() { return BufPtr; }
|
|
|
|
private:
|
|
using Storage = AlignedCharArrayUnion<TNorm>;
|
|
|
|
Storage Buffer;
|
|
IO &io;
|
|
TNorm *BufPtr = nullptr;
|
|
TFinal &Result;
|
|
};
|
|
|
|
///
|
|
/// The Input class is used to parse a yaml document into in-memory structs
|
|
/// and vectors.
|
|
///
|
|
/// It works by using YAMLParser to do a syntax parse of the entire yaml
|
|
/// document, then the Input class builds a graph of HNodes which wraps
|
|
/// each yaml Node. The extra layer is buffering. The low level yaml
|
|
/// parser only lets you look at each node once. The buffering layer lets
|
|
/// you search and interate multiple times. This is necessary because
|
|
/// the mapRequired() method calls may not be in the same order
|
|
/// as the keys in the document.
|
|
///
|
|
class Input : public IO {
|
|
public:
|
|
// Construct a yaml Input object from a StringRef and optional
|
|
// user-data. The DiagHandler can be specified to provide
|
|
// alternative error reporting.
|
|
Input(StringRef InputContent,
|
|
void *Ctxt = nullptr,
|
|
SourceMgr::DiagHandlerTy DiagHandler = nullptr,
|
|
void *DiagHandlerCtxt = nullptr);
|
|
~Input() override;
|
|
|
|
// Check if there was an syntax or semantic error during parsing.
|
|
std::error_code error();
|
|
|
|
private:
|
|
bool outputting() override;
|
|
bool mapTag(StringRef, bool) override;
|
|
void beginMapping() override;
|
|
void endMapping() override;
|
|
bool preflightKey(const char *, bool, bool, bool &, void *&) override;
|
|
void postflightKey(void *) override;
|
|
std::vector<StringRef> keys() override;
|
|
void beginFlowMapping() override;
|
|
void endFlowMapping() override;
|
|
unsigned beginSequence() override;
|
|
void endSequence() override;
|
|
bool preflightElement(unsigned index, void *&) override;
|
|
void postflightElement(void *) override;
|
|
unsigned beginFlowSequence() override;
|
|
bool preflightFlowElement(unsigned , void *&) override;
|
|
void postflightFlowElement(void *) override;
|
|
void endFlowSequence() override;
|
|
void beginEnumScalar() override;
|
|
bool matchEnumScalar(const char*, bool) override;
|
|
bool matchEnumFallback() override;
|
|
void endEnumScalar() override;
|
|
bool beginBitSetScalar(bool &) override;
|
|
bool bitSetMatch(const char *, bool ) override;
|
|
void endBitSetScalar() override;
|
|
void scalarString(StringRef &, bool) override;
|
|
void blockScalarString(StringRef &) override;
|
|
void setError(const Twine &message) override;
|
|
bool canElideEmptySequence() override;
|
|
|
|
class HNode {
|
|
virtual void anchor();
|
|
|
|
public:
|
|
HNode(Node *n) : _node(n) { }
|
|
virtual ~HNode() = default;
|
|
|
|
static bool classof(const HNode *) { return true; }
|
|
|
|
Node *_node;
|
|
};
|
|
|
|
class EmptyHNode : public HNode {
|
|
void anchor() override;
|
|
|
|
public:
|
|
EmptyHNode(Node *n) : HNode(n) { }
|
|
|
|
static bool classof(const HNode *n) { return NullNode::classof(n->_node); }
|
|
|
|
static bool classof(const EmptyHNode *) { return true; }
|
|
};
|
|
|
|
class ScalarHNode : public HNode {
|
|
void anchor() override;
|
|
|
|
public:
|
|
ScalarHNode(Node *n, StringRef s) : HNode(n), _value(s) { }
|
|
|
|
StringRef value() const { return _value; }
|
|
|
|
static bool classof(const HNode *n) {
|
|
return ScalarNode::classof(n->_node) ||
|
|
BlockScalarNode::classof(n->_node);
|
|
}
|
|
|
|
static bool classof(const ScalarHNode *) { return true; }
|
|
|
|
protected:
|
|
StringRef _value;
|
|
};
|
|
|
|
class MapHNode : public HNode {
|
|
void anchor() override;
|
|
|
|
public:
|
|
MapHNode(Node *n) : HNode(n) { }
|
|
|
|
static bool classof(const HNode *n) {
|
|
return MappingNode::classof(n->_node);
|
|
}
|
|
|
|
static bool classof(const MapHNode *) { return true; }
|
|
|
|
using NameToNode = StringMap<std::unique_ptr<HNode>>;
|
|
|
|
NameToNode Mapping;
|
|
SmallVector<std::string, 6> ValidKeys;
|
|
};
|
|
|
|
class SequenceHNode : public HNode {
|
|
void anchor() override;
|
|
|
|
public:
|
|
SequenceHNode(Node *n) : HNode(n) { }
|
|
|
|
static bool classof(const HNode *n) {
|
|
return SequenceNode::classof(n->_node);
|
|
}
|
|
|
|
static bool classof(const SequenceHNode *) { return true; }
|
|
|
|
std::vector<std::unique_ptr<HNode>> Entries;
|
|
};
|
|
|
|
std::unique_ptr<Input::HNode> createHNodes(Node *node);
|
|
void setError(HNode *hnode, const Twine &message);
|
|
void setError(Node *node, const Twine &message);
|
|
|
|
public:
|
|
// These are only used by operator>>. They could be private
|
|
// if those templated things could be made friends.
|
|
bool setCurrentDocument();
|
|
bool nextDocument();
|
|
|
|
/// Returns the current node that's being parsed by the YAML Parser.
|
|
const Node *getCurrentNode() const;
|
|
|
|
private:
|
|
SourceMgr SrcMgr; // must be before Strm
|
|
std::unique_ptr<llvm::yaml::Stream> Strm;
|
|
std::unique_ptr<HNode> TopNode;
|
|
std::error_code EC;
|
|
BumpPtrAllocator StringAllocator;
|
|
document_iterator DocIterator;
|
|
std::vector<bool> BitValuesUsed;
|
|
HNode *CurrentNode = nullptr;
|
|
bool ScalarMatchFound;
|
|
};
|
|
|
|
///
|
|
/// The Output class is used to generate a yaml document from in-memory structs
|
|
/// and vectors.
|
|
///
|
|
class Output : public IO {
|
|
public:
|
|
Output(raw_ostream &, void *Ctxt = nullptr, int WrapColumn = 70);
|
|
~Output() override;
|
|
|
|
/// \brief Set whether or not to output optional values which are equal
|
|
/// to the default value. By default, when outputting if you attempt
|
|
/// to write a value that is equal to the default, the value gets ignored.
|
|
/// Sometimes, it is useful to be able to see these in the resulting YAML
|
|
/// anyway.
|
|
void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; }
|
|
|
|
bool outputting() override;
|
|
bool mapTag(StringRef, bool) override;
|
|
void beginMapping() override;
|
|
void endMapping() override;
|
|
bool preflightKey(const char *key, bool, bool, bool &, void *&) override;
|
|
void postflightKey(void *) override;
|
|
std::vector<StringRef> keys() override;
|
|
void beginFlowMapping() override;
|
|
void endFlowMapping() override;
|
|
unsigned beginSequence() override;
|
|
void endSequence() override;
|
|
bool preflightElement(unsigned, void *&) override;
|
|
void postflightElement(void *) override;
|
|
unsigned beginFlowSequence() override;
|
|
bool preflightFlowElement(unsigned, void *&) override;
|
|
void postflightFlowElement(void *) override;
|
|
void endFlowSequence() override;
|
|
void beginEnumScalar() override;
|
|
bool matchEnumScalar(const char*, bool) override;
|
|
bool matchEnumFallback() override;
|
|
void endEnumScalar() override;
|
|
bool beginBitSetScalar(bool &) override;
|
|
bool bitSetMatch(const char *, bool ) override;
|
|
void endBitSetScalar() override;
|
|
void scalarString(StringRef &, bool) override;
|
|
void blockScalarString(StringRef &) override;
|
|
void setError(const Twine &message) override;
|
|
bool canElideEmptySequence() override;
|
|
|
|
// These are only used by operator<<. They could be private
|
|
// if that templated operator could be made a friend.
|
|
void beginDocuments();
|
|
bool preflightDocument(unsigned);
|
|
void postflightDocument();
|
|
void endDocuments();
|
|
|
|
private:
|
|
void output(StringRef s);
|
|
void outputUpToEndOfLine(StringRef s);
|
|
void newLineCheck();
|
|
void outputNewLine();
|
|
void paddedKey(StringRef key);
|
|
void flowKey(StringRef Key);
|
|
|
|
enum InState {
|
|
inSeq,
|
|
inFlowSeq,
|
|
inMapFirstKey,
|
|
inMapOtherKey,
|
|
inFlowMapFirstKey,
|
|
inFlowMapOtherKey
|
|
};
|
|
|
|
raw_ostream &Out;
|
|
int WrapColumn;
|
|
SmallVector<InState, 8> StateStack;
|
|
int Column = 0;
|
|
int ColumnAtFlowStart = 0;
|
|
int ColumnAtMapFlowStart = 0;
|
|
bool NeedBitValueComma = false;
|
|
bool NeedFlowSequenceComma = false;
|
|
bool EnumerationMatchFound = false;
|
|
bool NeedsNewLine = false;
|
|
bool WriteDefaultValues = false;
|
|
};
|
|
|
|
/// YAML I/O does conversion based on types. But often native data types
|
|
/// are just a typedef of built in intergral types (e.g. int). But the C++
|
|
/// type matching system sees through the typedef and all the typedefed types
|
|
/// look like a built in type. This will cause the generic YAML I/O conversion
|
|
/// to be used. To provide better control over the YAML conversion, you can
|
|
/// use this macro instead of typedef. It will create a class with one field
|
|
/// and automatic conversion operators to and from the base type.
|
|
/// Based on BOOST_STRONG_TYPEDEF
|
|
#define LLVM_YAML_STRONG_TYPEDEF(_base, _type) \
|
|
struct _type { \
|
|
_type() = default; \
|
|
_type(const _base v) : value(v) {} \
|
|
_type(const _type &v) = default; \
|
|
_type &operator=(const _type &rhs) = default; \
|
|
_type &operator=(const _base &rhs) { value = rhs; return *this; } \
|
|
operator const _base & () const { return value; } \
|
|
bool operator==(const _type &rhs) const { return value == rhs.value; } \
|
|
bool operator==(const _base &rhs) const { return value == rhs; } \
|
|
bool operator<(const _type &rhs) const { return value < rhs.value; } \
|
|
_base value; \
|
|
using BaseType = _base; \
|
|
};
|
|
|
|
///
|
|
/// Use these types instead of uintXX_t in any mapping to have
|
|
/// its yaml output formatted as hexadecimal.
|
|
///
|
|
LLVM_YAML_STRONG_TYPEDEF(uint8_t, Hex8)
|
|
LLVM_YAML_STRONG_TYPEDEF(uint16_t, Hex16)
|
|
LLVM_YAML_STRONG_TYPEDEF(uint32_t, Hex32)
|
|
LLVM_YAML_STRONG_TYPEDEF(uint64_t, Hex64)
|
|
|
|
template<>
|
|
struct ScalarTraits<Hex8> {
|
|
static void output(const Hex8 &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, Hex8 &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<Hex16> {
|
|
static void output(const Hex16 &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, Hex16 &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<Hex32> {
|
|
static void output(const Hex32 &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, Hex32 &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
template<>
|
|
struct ScalarTraits<Hex64> {
|
|
static void output(const Hex64 &, void *, raw_ostream &);
|
|
static StringRef input(StringRef, void *, Hex64 &);
|
|
static bool mustQuote(StringRef) { return false; }
|
|
};
|
|
|
|
// Define non-member operator>> so that Input can stream in a document list.
|
|
template <typename T>
|
|
inline
|
|
typename std::enable_if<has_DocumentListTraits<T>::value, Input &>::type
|
|
operator>>(Input &yin, T &docList) {
|
|
int i = 0;
|
|
EmptyContext Ctx;
|
|
while ( yin.setCurrentDocument() ) {
|
|
yamlize(yin, DocumentListTraits<T>::element(yin, docList, i), true, Ctx);
|
|
if ( yin.error() )
|
|
return yin;
|
|
yin.nextDocument();
|
|
++i;
|
|
}
|
|
return yin;
|
|
}
|
|
|
|
// Define non-member operator>> so that Input can stream in a map as a document.
|
|
template <typename T>
|
|
inline typename std::enable_if<has_MappingTraits<T, EmptyContext>::value,
|
|
Input &>::type
|
|
operator>>(Input &yin, T &docMap) {
|
|
EmptyContext Ctx;
|
|
yin.setCurrentDocument();
|
|
yamlize(yin, docMap, true, Ctx);
|
|
return yin;
|
|
}
|
|
|
|
// Define non-member operator>> so that Input can stream in a sequence as
|
|
// a document.
|
|
template <typename T>
|
|
inline
|
|
typename std::enable_if<has_SequenceTraits<T>::value, Input &>::type
|
|
operator>>(Input &yin, T &docSeq) {
|
|
EmptyContext Ctx;
|
|
if (yin.setCurrentDocument())
|
|
yamlize(yin, docSeq, true, Ctx);
|
|
return yin;
|
|
}
|
|
|
|
// Define non-member operator>> so that Input can stream in a block scalar.
|
|
template <typename T>
|
|
inline
|
|
typename std::enable_if<has_BlockScalarTraits<T>::value, Input &>::type
|
|
operator>>(Input &In, T &Val) {
|
|
EmptyContext Ctx;
|
|
if (In.setCurrentDocument())
|
|
yamlize(In, Val, true, Ctx);
|
|
return In;
|
|
}
|
|
|
|
// Define non-member operator>> so that Input can stream in a string map.
|
|
template <typename T>
|
|
inline
|
|
typename std::enable_if<has_CustomMappingTraits<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,
|
|
Input &>::type
|
|
operator>>(Input &yin, T &docSeq) {
|
|
char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)];
|
|
return yin;
|
|
}
|
|
|
|
// Define non-member operator<< so that Output can stream out document list.
|
|
template <typename T>
|
|
inline
|
|
typename std::enable_if<has_DocumentListTraits<T>::value, Output &>::type
|
|
operator<<(Output &yout, T &docList) {
|
|
EmptyContext Ctx;
|
|
yout.beginDocuments();
|
|
const size_t count = DocumentListTraits<T>::size(yout, docList);
|
|
for(size_t i=0; i < count; ++i) {
|
|
if ( yout.preflightDocument(i) ) {
|
|
yamlize(yout, DocumentListTraits<T>::element(yout, docList, i), true,
|
|
Ctx);
|
|
yout.postflightDocument();
|
|
}
|
|
}
|
|
yout.endDocuments();
|
|
return yout;
|
|
}
|
|
|
|
// Define non-member operator<< so that Output can stream out a map.
|
|
template <typename T>
|
|
inline typename std::enable_if<has_MappingTraits<T, EmptyContext>::value,
|
|
Output &>::type
|
|
operator<<(Output &yout, T &map) {
|
|
EmptyContext Ctx;
|
|
yout.beginDocuments();
|
|
if ( yout.preflightDocument(0) ) {
|
|
yamlize(yout, map, true, Ctx);
|
|
yout.postflightDocument();
|
|
}
|
|
yout.endDocuments();
|
|
return yout;
|
|
}
|
|
|
|
// Define non-member operator<< so that Output can stream out a sequence.
|
|
template <typename T>
|
|
inline
|
|
typename std::enable_if<has_SequenceTraits<T>::value, Output &>::type
|
|
operator<<(Output &yout, T &seq) {
|
|
EmptyContext Ctx;
|
|
yout.beginDocuments();
|
|
if ( yout.preflightDocument(0) ) {
|
|
yamlize(yout, seq, true, Ctx);
|
|
yout.postflightDocument();
|
|
}
|
|
yout.endDocuments();
|
|
return yout;
|
|
}
|
|
|
|
// Define non-member operator<< so that Output can stream out a block scalar.
|
|
template <typename T>
|
|
inline
|
|
typename std::enable_if<has_BlockScalarTraits<T>::value, Output &>::type
|
|
operator<<(Output &Out, T &Val) {
|
|
EmptyContext Ctx;
|
|
Out.beginDocuments();
|
|
if (Out.preflightDocument(0)) {
|
|
yamlize(Out, Val, true, Ctx);
|
|
Out.postflightDocument();
|
|
}
|
|
Out.endDocuments();
|
|
return Out;
|
|
}
|
|
|
|
// Define non-member operator<< so that Output can stream out a string map.
|
|
template <typename T>
|
|
inline
|
|
typename std::enable_if<has_CustomMappingTraits<T>::value, Output &>::type
|
|
operator<<(Output &Out, T &Val) {
|
|
EmptyContext Ctx;
|
|
Out.beginDocuments();
|
|
if (Out.preflightDocument(0)) {
|
|
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,
|
|
Output &>::type
|
|
operator<<(Output &yout, T &seq) {
|
|
char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)];
|
|
return yout;
|
|
}
|
|
|
|
template <bool B> struct IsFlowSequenceBase {};
|
|
template <> struct IsFlowSequenceBase<true> { static const bool flow = true; };
|
|
|
|
template <typename T, bool Flow>
|
|
struct SequenceTraitsImpl : IsFlowSequenceBase<Flow> {
|
|
private:
|
|
using type = typename T::value_type;
|
|
|
|
public:
|
|
static size_t size(IO &io, T &seq) { return seq.size(); }
|
|
|
|
static type &element(IO &io, T &seq, size_t index) {
|
|
if (index >= seq.size())
|
|
seq.resize(index + 1);
|
|
return seq[index];
|
|
}
|
|
};
|
|
|
|
// Simple helper to check an expression can be used as a bool-valued template
|
|
// argument.
|
|
template <bool> struct CheckIsBool { static const bool value = true; };
|
|
|
|
// If T has SequenceElementTraits, then vector<T> and SmallVector<T, N> have
|
|
// SequenceTraits that do the obvious thing.
|
|
template <typename T>
|
|
struct SequenceTraits<std::vector<T>,
|
|
typename std::enable_if<CheckIsBool<
|
|
SequenceElementTraits<T>::flow>::value>::type>
|
|
: SequenceTraitsImpl<std::vector<T>, SequenceElementTraits<T>::flow> {};
|
|
template <typename T, unsigned N>
|
|
struct SequenceTraits<SmallVector<T, N>,
|
|
typename std::enable_if<CheckIsBool<
|
|
SequenceElementTraits<T>::flow>::value>::type>
|
|
: SequenceTraitsImpl<SmallVector<T, N>, SequenceElementTraits<T>::flow> {};
|
|
|
|
// Sequences of fundamental types use flow formatting.
|
|
template <typename T>
|
|
struct SequenceElementTraits<
|
|
T, typename std::enable_if<std::is_fundamental<T>::value>::type> {
|
|
static const bool flow = true;
|
|
};
|
|
|
|
// Sequences of strings use block formatting.
|
|
template<> struct SequenceElementTraits<std::string> {
|
|
static const bool flow = false;
|
|
};
|
|
template<> struct SequenceElementTraits<StringRef> {
|
|
static const bool flow = false;
|
|
};
|
|
template<> struct SequenceElementTraits<std::pair<std::string, std::string>> {
|
|
static const bool flow = false;
|
|
};
|
|
|
|
/// Implementation of CustomMappingTraits for std::map<std::string, T>.
|
|
template <typename T> struct StdMapStringCustomMappingTraitsImpl {
|
|
using map_type = std::map<std::string, T>;
|
|
|
|
static void inputOne(IO &io, StringRef key, map_type &v) {
|
|
io.mapRequired(key.str().c_str(), v[key]);
|
|
}
|
|
|
|
static void output(IO &io, map_type &v) {
|
|
for (auto &p : v)
|
|
io.mapRequired(p.first.c_str(), p.second);
|
|
}
|
|
};
|
|
|
|
} // end namespace yaml
|
|
} // end namespace llvm
|
|
|
|
#define LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(TYPE, FLOW) \
|
|
namespace llvm { \
|
|
namespace yaml { \
|
|
static_assert( \
|
|
!std::is_fundamental<TYPE>::value && \
|
|
!std::is_same<TYPE, std::string>::value && \
|
|
!std::is_same<TYPE, llvm::StringRef>::value, \
|
|
"only use LLVM_YAML_IS_SEQUENCE_VECTOR for types you control"); \
|
|
template <> struct SequenceElementTraits<TYPE> { \
|
|
static const bool flow = FLOW; \
|
|
}; \
|
|
} \
|
|
}
|
|
|
|
/// Utility for declaring that a std::vector of a particular type
|
|
/// should be considered a YAML sequence.
|
|
#define LLVM_YAML_IS_SEQUENCE_VECTOR(type) \
|
|
LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(type, false)
|
|
|
|
/// Utility for declaring that a std::vector of a particular type
|
|
/// should be considered a YAML flow sequence.
|
|
#define LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(type) \
|
|
LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(type, true)
|
|
|
|
#define LLVM_YAML_DECLARE_MAPPING_TRAITS(Type) \
|
|
namespace llvm { \
|
|
namespace yaml { \
|
|
template <> struct MappingTraits<Type> { \
|
|
static void mapping(IO &IO, Type &Obj); \
|
|
}; \
|
|
} \
|
|
}
|
|
|
|
#define LLVM_YAML_DECLARE_ENUM_TRAITS(Type) \
|
|
namespace llvm { \
|
|
namespace yaml { \
|
|
template <> struct ScalarEnumerationTraits<Type> { \
|
|
static void enumeration(IO &io, Type &Value); \
|
|
}; \
|
|
} \
|
|
}
|
|
|
|
#define LLVM_YAML_DECLARE_BITSET_TRAITS(Type) \
|
|
namespace llvm { \
|
|
namespace yaml { \
|
|
template <> struct ScalarBitSetTraits<Type> { \
|
|
static void bitset(IO &IO, Type &Options); \
|
|
}; \
|
|
} \
|
|
}
|
|
|
|
#define LLVM_YAML_DECLARE_SCALAR_TRAITS(Type, MustQuote) \
|
|
namespace llvm { \
|
|
namespace yaml { \
|
|
template <> struct ScalarTraits<Type> { \
|
|
static void output(const Type &Value, void *ctx, raw_ostream &Out); \
|
|
static StringRef input(StringRef Scalar, void *ctxt, Type &Value); \
|
|
static bool mustQuote(StringRef) { return MustQuote; } \
|
|
}; \
|
|
} \
|
|
}
|
|
|
|
/// Utility for declaring that a std::vector of a particular type
|
|
/// should be considered a YAML document list.
|
|
#define LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(_type) \
|
|
namespace llvm { \
|
|
namespace yaml { \
|
|
template <unsigned N> \
|
|
struct DocumentListTraits<SmallVector<_type, N>> \
|
|
: public SequenceTraitsImpl<SmallVector<_type, N>, false> {}; \
|
|
template <> \
|
|
struct DocumentListTraits<std::vector<_type>> \
|
|
: public SequenceTraitsImpl<std::vector<_type>, false> {}; \
|
|
} \
|
|
}
|
|
|
|
/// Utility for declaring that std::map<std::string, _type> should be considered
|
|
/// a YAML map.
|
|
#define LLVM_YAML_IS_STRING_MAP(_type) \
|
|
namespace llvm { \
|
|
namespace yaml { \
|
|
template <> \
|
|
struct CustomMappingTraits<std::map<std::string, _type>> \
|
|
: public StdMapStringCustomMappingTraitsImpl<_type> {}; \
|
|
} \
|
|
}
|
|
|
|
#endif // LLVM_SUPPORT_YAMLTRAITS_H
|