diff --git a/llvm/include/llvm/Support/YAMLGenerateSchema.h b/llvm/include/llvm/Support/YAMLGenerateSchema.h new file mode 100644 index 0000000000000..7b4781a3dc18f --- /dev/null +++ b/llvm/include/llvm/Support/YAMLGenerateSchema.h @@ -0,0 +1,400 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_YAMLGENERATE_SCHEMA_H +#define LLVM_SUPPORT_YAMLGENERATE_SCHEMA_H + +#include "llvm/Support/Casting.h" +#include "llvm/Support/YAMLTraits.h" + +namespace llvm { + +namespace json { +class Value; +} + +namespace yaml { + +class GenerateSchema : public IO { +public: + GenerateSchema(raw_ostream &RO); + ~GenerateSchema() override = default; + + IOKind getKind() const override; + bool outputting() const override; + bool mapTag(StringRef, bool) override; + void beginMapping() override; + void endMapping() override; + bool preflightKey(StringRef, bool, bool, bool &, void *&) override; + void postflightKey(void *) override; + std::vector 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(StringRef, bool) override; + bool matchEnumFallback() override; + void endEnumScalar() override; + bool beginBitSetScalar(bool &) override; + bool bitSetMatch(StringRef, bool) override; + 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; + std::error_code error() override; + bool canElideEmptySequence() override; + + bool preflightDocument(); + void postflightDocument(); + + class SchemaNode { + public: + virtual json::Value toJSON() const = 0; + + virtual ~SchemaNode() = default; + }; + + enum class PropertyKind : uint8_t { + UserDefined, + Properties, + AdditionalProperties, + Required, + Optional, + Type, + Enum, + Items, + FlowStyle, + }; + + class SchemaProperty : public SchemaNode { + StringRef Name; + PropertyKind Kind; + + public: + SchemaProperty(StringRef Name, PropertyKind Kind) + : Name(Name), Kind(Kind) {} + + PropertyKind getKind() const { return Kind; } + + StringRef getName() const { return Name; } + }; + + class Schema; + + class UserDefinedProperty final : public SchemaProperty { + Schema *Value; + + public: + UserDefinedProperty(StringRef Name, Schema *Value) + : SchemaProperty(Name, PropertyKind::UserDefined), Value(Value) {} + + Schema *getSchema() const { return Value; } + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::UserDefined; + } + }; + + class PropertiesProperty final : public SchemaProperty, + SmallVector { + public: + using BaseVector = SmallVector; + + PropertiesProperty() + : SchemaProperty("properties", PropertyKind::Properties) {} + + using BaseVector::begin; + using BaseVector::emplace_back; + using BaseVector::end; + using BaseVector::size; + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Properties; + } + }; + + class AdditionalPropertiesProperty final : public SchemaProperty { + Schema *Value; + + public: + AdditionalPropertiesProperty(Schema *Value = nullptr) + : SchemaProperty("additionalProperties", + PropertyKind::AdditionalProperties), + Value(Value) {} + + Schema *getSchema() const { return Value; } + + void setSchema(Schema *S) { Value = S; } + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::AdditionalProperties; + } + }; + + class RequiredProperty final : public SchemaProperty, + SmallVector { + public: + using BaseVector = SmallVector; + + RequiredProperty() : SchemaProperty("required", PropertyKind::Required) {} + + using BaseVector::begin; + using BaseVector::emplace_back; + using BaseVector::end; + using BaseVector::size; + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Required; + } + }; + + class OptionalProperty final : public SchemaProperty, + SmallVector { + public: + using BaseVector = SmallVector; + + OptionalProperty() : SchemaProperty("optional", PropertyKind::Optional) {} + + using BaseVector::begin; + using BaseVector::emplace_back; + using BaseVector::end; + using BaseVector::size; + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Optional; + } + }; + + class TypeProperty final : public SchemaProperty { + StringRef Value; + + public: + TypeProperty(StringRef Value) + : SchemaProperty("type", PropertyKind::Type), Value(Value) {} + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Type; + } + }; + + class EnumProperty final : public SchemaProperty, SmallVector { + public: + using BaseVector = SmallVector; + + EnumProperty() : SchemaProperty("enum", PropertyKind::Enum) {} + + using BaseVector::begin; + using BaseVector::emplace_back; + using BaseVector::end; + using BaseVector::size; + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Enum; + } + }; + + class ItemsProperty final : public SchemaProperty { + Schema *Value; + + public: + ItemsProperty(Schema *Value = nullptr) + : SchemaProperty("items", PropertyKind::Items), Value(Value) {} + + Schema *getSchema() const { return Value; } + + void setSchema(Schema *S) { Value = S; } + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Items; + } + }; + + enum class FlowStyle : bool { + Block, + Flow, + }; + + class FlowStyleProperty final : public SchemaProperty { + FlowStyle Style; + + public: + FlowStyleProperty(FlowStyle Style = FlowStyle::Block) + : SchemaProperty("flowStyle", PropertyKind::FlowStyle), Style(Style) {} + + void setStyle(FlowStyle S) { Style = S; } + + FlowStyle getStyle() const { return Style; } + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::FlowStyle; + } + }; + + class Schema final : public SchemaNode, SmallVector { + public: + using BaseVector = SmallVector; + + Schema() = default; + + using BaseVector::begin; + using BaseVector::emplace_back; + using BaseVector::end; + using BaseVector::size; + + json::Value toJSON() const override; + }; + +private: + std::vector> SchemaNodes; + SmallVector Schemas; + raw_ostream &RO; + SchemaNode *Root = nullptr; + + template + PropertyType *createProperty(PropertyArgs &&...Args) { + std::unique_ptr UPtr = + std::make_unique(std::forward(Args)...); + PropertyType *Ptr = UPtr.get(); + SchemaNodes.emplace_back(std::move(UPtr)); + return Ptr; + } + + template + PropertyType *getOrCreateProperty(Schema &S, PropertyArgs... Args) { + auto Found = std::find_if(S.begin(), S.end(), [](SchemaProperty *Property) { + return isa(Property); + }); + if (Found != S.end()) { + return cast(*Found); + } + PropertyType *Created = + createProperty(std::forward(Args)...); + S.emplace_back(Created); + return Created; + } + + Schema *createSchema() { + std::unique_ptr UPtr = std::make_unique(); + Schema *Ptr = UPtr.get(); + SchemaNodes.emplace_back(std::move(UPtr)); + return Ptr; + } + + Schema *getTopSchema() const { + return Schemas.empty() ? nullptr : Schemas.back(); + } +}; + +// Define non-member operator<< so that Output can stream out document list. +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &DocList) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, DocumentListTraits::element(Gen, DocList, 0), true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Define non-member operator<< so that Output can stream out a map. +template +inline std::enable_if_t::value, + GenerateSchema &> +operator<<(GenerateSchema &Gen, T &Map) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, Map, true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Define non-member operator<< so that Output can stream out a sequence. +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &Seq) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, Seq, true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Define non-member operator<< so that Output can stream out a block scalar. +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &Val) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, Val, true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Define non-member operator<< so that Output can stream out a string map. +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &Val) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, Val, true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Define non-member operator<< so that Output can stream out a polymorphic +// type. +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &Val) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, Val, true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Provide better error message about types missing a trait specialization +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &seq) { + char missing_yaml_trait_for_type[sizeof(MissingTrait)]; + return Gen; +} + +} // namespace yaml + +} // namespace llvm + +#endif // LLVM_SUPPORT_YAMLGENERATE_SCHEMA_H diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h index 3d36f41ca1a04..435323ef9ffbd 100644 --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -683,12 +683,19 @@ struct unvalidatedMappingTraits !has_MappingValidateTraits::value> { }; +enum class IOKind : uint8_t { + Outputting, + Inputting, + GeneratingSchema, +}; + // Base class for Input and Output. class LLVM_ABI IO { public: IO(void *Ctxt = nullptr); virtual ~IO(); + virtual IOKind getKind() const = 0; virtual bool outputting() const = 0; virtual unsigned beginSequence() = 0; @@ -732,7 +739,8 @@ class LLVM_ABI IO { virtual void setAllowUnknownKeys(bool Allow); template void enumCase(T &Val, StringRef Str, const T ConstVal) { - if (matchEnumScalar(Str, outputting() && Val == ConstVal)) { + if (matchEnumScalar(Str, + getKind() == IOKind::Outputting && Val == ConstVal)) { Val = ConstVal; } } @@ -740,7 +748,8 @@ class LLVM_ABI IO { // allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF template void enumCase(T &Val, StringRef Str, const uint32_t ConstVal) { - if (matchEnumScalar(Str, outputting() && Val == static_cast(ConstVal))) { + if (matchEnumScalar(Str, getKind() == IOKind::Outputting && + Val == static_cast(ConstVal))) { Val = ConstVal; } } @@ -757,7 +766,8 @@ class LLVM_ABI IO { template void bitSetCase(T &Val, StringRef Str, const T ConstVal) { - if (bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal)) { + if (bitSetMatch(Str, getKind() == IOKind::Outputting && + (Val & ConstVal) == ConstVal)) { Val = static_cast(Val | ConstVal); } } @@ -765,21 +775,24 @@ class LLVM_ABI IO { // allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF template void bitSetCase(T &Val, StringRef Str, const uint32_t ConstVal) { - if (bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal)) { + if (bitSetMatch(Str, getKind() == IOKind::Outputting && + (Val & ConstVal) == ConstVal)) { Val = static_cast(Val | ConstVal); } } template void maskedBitSetCase(T &Val, StringRef Str, T ConstVal, T Mask) { - if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal)) + if (bitSetMatch(Str, getKind() == IOKind::Outputting && + (Val & Mask) == ConstVal)) Val = Val | ConstVal; } template void maskedBitSetCase(T &Val, StringRef Str, uint32_t ConstVal, uint32_t Mask) { - if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal)) + if (bitSetMatch(Str, getKind() == IOKind::Outputting && + (Val & Mask) == ConstVal)) Val = Val | ConstVal; } @@ -844,7 +857,8 @@ class LLVM_ABI IO { bool Required, Context &Ctx) { void *SaveInfo; bool UseDefault; - const bool sameAsDefault = outputting() && Val == DefaultValue; + const bool sameAsDefault = + (getKind() == IOKind::Outputting) && Val == DefaultValue; if (this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { yamlize(*this, Val, Required, Ctx); @@ -905,7 +919,7 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { template std::enable_if_t::value, void> yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { SmallString<128> Storage; raw_svector_ostream Buffer(Storage); ScalarTraits::output(Val, io.getContext(), Buffer); @@ -924,7 +938,7 @@ std::enable_if_t::value, void> yamlize(IO &io, T &Val, bool, template std::enable_if_t::value, void> yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) { - if (YamlIO.outputting()) { + if (YamlIO.getKind() == IOKind::Outputting) { std::string Storage; raw_string_ostream Buffer(Storage); BlockScalarTraits::output(Val, YamlIO.getContext(), Buffer); @@ -943,7 +957,7 @@ yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) { template std::enable_if_t::value, void> yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { std::string ScalarStorage, TagStorage; raw_string_ostream ScalarBuffer(ScalarStorage), TagBuffer(TagStorage); TaggedScalarTraits::output(Val, io.getContext(), ScalarBuffer, @@ -985,7 +999,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) { io.beginFlowMapping(); else io.beginMapping(); - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { std::string Err = detail::doValidate(io, Val, Ctx); if (!Err.empty()) { errs() << Err << "\n"; @@ -993,7 +1007,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) { } } detail::doMapping(io, Val, Ctx); - if (!io.outputting()) { + if (io.getKind() == IOKind::Inputting) { std::string Err = detail::doValidate(io, Val, Ctx); if (!Err.empty()) io.setError(Err); @@ -1007,7 +1021,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) { template bool yamlizeMappingEnumInput(IO &io, T &Val) { if constexpr (has_MappingEnumInputTraits::value) { - if (io.outputting()) + if (io.getKind() == IOKind::Outputting) return false; io.beginEnumScalar(); @@ -1038,7 +1052,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) { template std::enable_if_t::value, void> yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { io.beginMapping(); CustomMappingTraits::output(io, Val); io.endMapping(); @@ -1053,8 +1067,9 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { template std::enable_if_t::value, void> yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { - switch (io.outputting() ? PolymorphicTraits::getKind(Val) - : io.getNodeKind()) { + switch (io.getKind() == IOKind::Outputting + ? PolymorphicTraits::getKind(Val) + : io.getNodeKind()) { case NodeKind::Scalar: return yamlize(io, PolymorphicTraits::getAsScalar(Val), true, Ctx); case NodeKind::Map: @@ -1075,7 +1090,9 @@ std::enable_if_t::value, void> yamlize(IO &io, T &Seq, bool, Context &Ctx) { if (has_FlowTraits>::value) { unsigned incnt = io.beginFlowSequence(); - unsigned count = io.outputting() ? SequenceTraits::size(io, Seq) : incnt; + unsigned count = io.getKind() == IOKind::Outputting + ? SequenceTraits::size(io, Seq) + : incnt; for (unsigned i = 0; i < count; ++i) { void *SaveInfo; if (io.preflightFlowElement(i, SaveInfo)) { @@ -1086,7 +1103,9 @@ yamlize(IO &io, T &Seq, bool, Context &Ctx) { io.endFlowSequence(); } else { unsigned incnt = io.beginSequence(); - unsigned count = io.outputting() ? SequenceTraits::size(io, Seq) : incnt; + unsigned count = io.getKind() == IOKind::Outputting + ? SequenceTraits::size(io, Seq) + : incnt; for (unsigned i = 0; i < count; ++i) { void *SaveInfo; if (io.preflightElement(i, SaveInfo)) { @@ -1239,7 +1258,7 @@ struct ScalarBitSetTraits< template struct MappingNormalization { MappingNormalization(IO &i_o, TFinal &Obj) : io(i_o), BufPtr(nullptr), Result(Obj) { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { BufPtr = new (&Buffer) TNorm(io, Obj); } else { BufPtr = new (&Buffer) TNorm(io); @@ -1247,7 +1266,7 @@ template struct MappingNormalization { } ~MappingNormalization() { - if (!io.outputting()) { + if (io.getKind() != IOKind::Outputting) { Result = BufPtr->denormalize(io); } BufPtr->~TNorm(); @@ -1269,7 +1288,7 @@ template struct MappingNormalization { template struct MappingNormalizationHeap { MappingNormalizationHeap(IO &i_o, TFinal &Obj, BumpPtrAllocator *allocator) : io(i_o), Result(Obj) { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { BufPtr = new (&Buffer) TNorm(io, Obj); } else if (allocator) { BufPtr = allocator->Allocate(); @@ -1280,7 +1299,7 @@ template struct MappingNormalizationHeap { } ~MappingNormalizationHeap() { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { BufPtr->~TNorm(); } else { Result = BufPtr->denormalize(io); @@ -1327,6 +1346,7 @@ class LLVM_ABI Input : public IO { std::error_code error() override; private: + IOKind getKind() const override; bool outputting() const override; bool mapTag(StringRef, bool) override; void beginMapping() override; @@ -1478,6 +1498,7 @@ class LLVM_ABI Output : public IO { /// anyway. void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; } + IOKind getKind() const override; bool outputting() const override; bool mapTag(StringRef, bool) override; void beginMapping() override; @@ -1563,8 +1584,8 @@ void IO::processKeyWithDefault(StringRef Key, std::optional &Val, assert(!DefaultValue && "std::optional shouldn't have a value!"); void *SaveInfo; bool UseDefault = true; - const bool sameAsDefault = outputting() && !Val; - if (!outputting() && !Val) + const bool sameAsDefault = (getKind() == IOKind::Outputting) && !Val; + if (getKind() != IOKind::Outputting && !Val) Val = T(); if (Val && this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { @@ -1574,7 +1595,7 @@ void IO::processKeyWithDefault(StringRef Key, std::optional &Val, // was requested, i.e. the DefaultValue will be assigned. The DefaultValue // is usually None. bool IsNone = false; - if (!outputting()) + if (getKind() == IOKind::Inputting) if (const auto *Node = dyn_cast(((Input *)this)->getCurrentNode())) // We use rtrim to ignore possible white spaces that might exist when a diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index 671a5fe941cef..6efb8c29249d6 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -282,6 +282,7 @@ add_llvm_component_library(LLVMSupport WithColor.cpp YAMLParser.cpp YAMLTraits.cpp + YAMLGenerateSchema.cpp raw_os_ostream.cpp raw_ostream.cpp raw_ostream_proxy.cpp diff --git a/llvm/lib/Support/YAMLGenerateSchema.cpp b/llvm/lib/Support/YAMLGenerateSchema.cpp new file mode 100644 index 0000000000000..bb10f84fdec5d --- /dev/null +++ b/llvm/lib/Support/YAMLGenerateSchema.cpp @@ -0,0 +1,285 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/YAMLGenerateSchema.h" +#include "llvm/Support/JSON.h" + +using namespace llvm; +using namespace yaml; + +//===----------------------------------------------------------------------===// +// GenerateSchema +//===----------------------------------------------------------------------===// + +GenerateSchema::GenerateSchema(raw_ostream &RO) : RO(RO) {} + +IOKind GenerateSchema::getKind() const { return IOKind::GeneratingSchema; } + +bool GenerateSchema::outputting() const { return false; } + +bool GenerateSchema::mapTag(StringRef, bool) { return false; } + +void GenerateSchema::beginMapping() { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("object"); + Top->emplace_back(Type); + FlowStyleProperty *FlowStyle = createProperty(); + Top->emplace_back(FlowStyle); +} + +void GenerateSchema::endMapping() {} + +bool GenerateSchema::preflightKey(StringRef Key, bool Required, + bool SameAsDefault, bool &UseDefault, + void *&SaveInfo) { + Schema *Top = getTopSchema(); + assert(Top); + if (Key == "additionalProperties") { + AdditionalPropertiesProperty *Additional = + getOrCreateProperty(*Top); + Schema *S = createSchema(); + Additional->setSchema(S); + Schemas.push_back(S); + return true; + } + + if (Required) { + RequiredProperty *Req = getOrCreateProperty(*Top); + Req->emplace_back(Key); + } else { + OptionalProperty *Opt = getOrCreateProperty(*Top); + Opt->emplace_back(Key); + } + PropertiesProperty *Properties = + getOrCreateProperty(*Top); + Schema *S = createSchema(); + UserDefinedProperty *UserDefined = + createProperty(Key, S); + Properties->emplace_back(UserDefined); + Schemas.push_back(S); + return true; +} + +void GenerateSchema::postflightKey(void *) { + assert(!Schemas.empty()); + Schemas.pop_back(); +} + +std::vector GenerateSchema::keys() { return {}; } + +void GenerateSchema::beginFlowMapping() { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("object"); + Top->emplace_back(Type); + FlowStyleProperty *FlowStyle = + createProperty(FlowStyle::Flow); + Top->emplace_back(FlowStyle); +} + +void GenerateSchema::endFlowMapping() {} + +unsigned GenerateSchema::beginSequence() { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("array"); + Top->emplace_back(Type); + ItemsProperty *Items = createProperty(); + Top->emplace_back(Items); + FlowStyleProperty *FlowStyle = createProperty(); + Top->emplace_back(FlowStyle); + return 1; +} + +void GenerateSchema::endSequence() {} + +bool GenerateSchema::preflightElement(unsigned, void *&) { + Schema *Top = getTopSchema(); + assert(Top); + Schema *S = createSchema(); + ItemsProperty *Items = getOrCreateProperty(*Top); + Items->setSchema(S); + Schemas.push_back(S); + return true; +} + +void GenerateSchema::postflightElement(void *) { + assert(!Schemas.empty()); + Schemas.pop_back(); +} + +unsigned GenerateSchema::beginFlowSequence() { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("array"); + Top->emplace_back(Type); + ItemsProperty *Items = createProperty(); + Top->emplace_back(Items); + FlowStyleProperty *FlowStyle = + createProperty(FlowStyle::Flow); + Top->emplace_back(FlowStyle); + return 1; +} + +bool GenerateSchema::preflightFlowElement(unsigned Arg1, void *&Arg2) { + return preflightElement(Arg1, Arg2); +} + +void GenerateSchema::postflightFlowElement(void *Arg1) { + postflightElement(Arg1); +} + +void GenerateSchema::endFlowSequence() { endSequence(); } + +void GenerateSchema::beginEnumScalar() { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("string"); + Top->emplace_back(Type); + EnumProperty *Enum = createProperty(); + Top->emplace_back(Enum); +} + +bool GenerateSchema::matchEnumScalar(StringRef Val, bool) { + Schema *Top = getTopSchema(); + assert(Top); + EnumProperty *Enum = getOrCreateProperty(*Top); + Enum->emplace_back(Val); + return false; +} + +bool GenerateSchema::matchEnumFallback() { return false; } + +void GenerateSchema::endEnumScalar() {} + +bool GenerateSchema::beginBitSetScalar(bool &) { + beginEnumScalar(); + return true; +} + +bool GenerateSchema::bitSetMatch(StringRef Val, bool Arg) { + return matchEnumScalar(Val, Arg); +} + +void GenerateSchema::endBitSetScalar() { endEnumScalar(); } + +void GenerateSchema::scalarString(StringRef &Val, QuotingType) { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("string"); + Top->emplace_back(Type); +} + +void GenerateSchema::blockScalarString(StringRef &S) { + scalarString(S, QuotingType::None); +} + +void GenerateSchema::scalarTag(std::string &val) {} + +NodeKind GenerateSchema::getNodeKind() { report_fatal_error("invalid call"); } + +void GenerateSchema::setError(const Twine &) {} + +std::error_code GenerateSchema::error() { return {}; } + +bool GenerateSchema::canElideEmptySequence() { return false; } + +// These are only used by operator<<. They could be private +// if that templated operator could be made a friend. + +bool GenerateSchema::preflightDocument() { + Schema *S = createSchema(); + Root = S; + Schemas.push_back(S); + return true; +} + +void GenerateSchema::postflightDocument() { + assert(!Schemas.empty()); + Schemas.pop_back(); + json::Value JSONValue = Root->toJSON(); + RO << llvm::formatv("{0:2}\n", JSONValue); +} + +json::Value GenerateSchema::UserDefinedProperty::toJSON() const { + return Value->toJSON(); +} + +json::Value GenerateSchema::PropertiesProperty::toJSON() const { + json::Object JSONObject; + for (UserDefinedProperty *Property : *this) { + json::Value JSONValue = Property->toJSON(); + JSONObject.try_emplace(Property->getName().data(), std::move(JSONValue)); + } + return JSONObject; +} + +json::Value GenerateSchema::AdditionalPropertiesProperty::toJSON() const { + return Value->toJSON(); +} + +json::Value GenerateSchema::RequiredProperty::toJSON() const { + json::Array JSONArray; + for (StringRef Value : *this) { + json::Value JSONValue(Value.data()); + JSONArray.emplace_back(std::move(JSONValue)); + } + return JSONArray; +} + +json::Value GenerateSchema::OptionalProperty::toJSON() const { + json::Array JSONArray; + for (StringRef Value : *this) { + json::Value JSONValue(Value.data()); + JSONArray.emplace_back(std::move(JSONValue)); + } + return JSONArray; +} + +json::Value GenerateSchema::TypeProperty::toJSON() const { + json::Value JSONValue(Value.data()); + return JSONValue; +} + +json::Value GenerateSchema::EnumProperty::toJSON() const { + json::Array JSONArray; + for (StringRef Value : *this) { + json::Value JSONValue(Value.data()); + JSONArray.emplace_back(std::move(JSONValue)); + } + return JSONArray; +} + +json::Value GenerateSchema::ItemsProperty::toJSON() const { + return Value->toJSON(); +} + +json::Value GenerateSchema::FlowStyleProperty::toJSON() const { + StringRef Value; + switch (Style) { + case FlowStyle::Block: + Value = "block"; + break; + case FlowStyle::Flow: + Value = "flow"; + break; + } + json::Value JSONValue(Value.data()); + return JSONValue; +} + +json::Value GenerateSchema::Schema::toJSON() const { + json::Object JSONObject; + for (SchemaProperty *Value : *this) { + json::Value JSONValue = Value->toJSON(); + StringRef Key = Value->getName(); + JSONObject.try_emplace(Key.data(), std::move(JSONValue)); + } + return JSONObject; +} diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp index 95a41eafdf5e4..6a0920efdbbcb 100644 --- a/llvm/lib/Support/YAMLTraits.cpp +++ b/llvm/lib/Support/YAMLTraits.cpp @@ -74,9 +74,9 @@ Input::~Input() = default; std::error_code Input::error() { return EC; } -bool Input::outputting() const { - return false; -} +IOKind Input::getKind() const { return IOKind::Inputting; } + +bool Input::outputting() const { return false; } bool Input::setCurrentDocument() { if (DocIterator != Strm->end()) { @@ -485,9 +485,9 @@ Output::Output(raw_ostream &yout, void *context, int WrapColumn) Output::~Output() = default; -bool Output::outputting() const { - return true; -} +IOKind Output::getKind() const { return IOKind::Outputting; } + +bool Output::outputting() const { return true; } void Output::beginMapping() { StateStack.push_back(inMapFirstKey); diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index 21f10eb610f11..850661de95264 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -112,6 +112,7 @@ add_llvm_unittest(SupportTests VirtualOutputFileTest.cpp WithColorTest.cpp YAMLIOTest.cpp + YAMLGenerateSchemaTest.cpp YAMLParserTest.cpp buffer_ostream_test.cpp formatted_raw_ostream_test.cpp diff --git a/llvm/unittests/Support/YAMLGenerateSchemaTest.cpp b/llvm/unittests/Support/YAMLGenerateSchemaTest.cpp new file mode 100644 index 0000000000000..82443c5745c9f --- /dev/null +++ b/llvm/unittests/Support/YAMLGenerateSchemaTest.cpp @@ -0,0 +1,125 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/YAMLGenerateSchema.h" +#include "llvm/Support/YAMLTraits.h" +#include +#include +#include + +#include "gtest/gtest.h" + +using namespace llvm; + +enum class ColorTy { + White, + Black, + Blue, +}; + +struct Baby { + std::string Name; + ColorTy Color; +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(Baby) + +struct Animal { + std::string Name; + std::optional Age; + std::vector Babies; +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(Animal) + +namespace llvm { +namespace yaml { + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, ColorTy &value) { + io.enumCase(value, "white", ColorTy::White); + io.enumCase(value, "black", ColorTy::Black); + io.enumCase(value, "blue", ColorTy::Blue); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, Baby &info) { + io.mapRequired("name", info.Name); + io.mapRequired("color", info.Color); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, Animal &info) { + io.mapRequired("name", info.Name); + io.mapOptional("age", info.Age); + io.mapOptional("babies", info.Babies); + } +}; + +} // namespace yaml +} // namespace llvm + +TEST(ObjectYAMLGenerateSchema, SimpleSchema) { + std::string String; + raw_string_ostream OS(String); + std::vector Animals; + yaml::GenerateSchema Gen(OS); + Gen << Animals; + StringRef YAMLSchema = R"({ + "flowStyle": "block", + "items": { + "flowStyle": "block", + "optional": [ + "age", + "babies" + ], + "properties": { + "age": { + "type": "string" + }, + "babies": { + "flowStyle": "block", + "items": { + "flowStyle": "block", + "properties": { + "color": { + "enum": [ + "white", + "black", + "blue" + ], + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "color" + ], + "type": "object" + }, + "type": "array" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "type": "array" +} +)"; + EXPECT_EQ(String.c_str(), YAMLSchema.str()); +} diff --git a/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn index df9ddf91f2c49..dd3932751a194 100644 --- a/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn @@ -178,6 +178,7 @@ static_library("Support") { "WithColor.cpp", "YAMLParser.cpp", "YAMLTraits.cpp", + "YAMLGenerateSchema.cpp", "Z3Solver.cpp", "circular_raw_ostream.cpp", "raw_os_ostream.cpp",