10b57cec5SDimitry Andric //===-- MsgPackDocumentYAML.cpp - MsgPack Document YAML interface -------*-===//
20b57cec5SDimitry Andric //
3*349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric /// This file implements YAMLIO on a msgpack::Document.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric
130b57cec5SDimitry Andric #include "llvm/BinaryFormat/MsgPackDocument.h"
140b57cec5SDimitry Andric #include "llvm/Support/YAMLTraits.h"
150b57cec5SDimitry Andric
160b57cec5SDimitry Andric using namespace llvm;
170b57cec5SDimitry Andric using namespace msgpack;
180b57cec5SDimitry Andric
190b57cec5SDimitry Andric namespace {
200b57cec5SDimitry Andric
210b57cec5SDimitry Andric // Struct used to represent scalar node. (MapDocNode and ArrayDocNode already
220b57cec5SDimitry Andric // exist in MsgPackDocument.h.)
230b57cec5SDimitry Andric struct ScalarDocNode : DocNode {
ScalarDocNode__anonbd77ee450111::ScalarDocNode240b57cec5SDimitry Andric ScalarDocNode(DocNode N) : DocNode(N) {}
250b57cec5SDimitry Andric
260b57cec5SDimitry Andric /// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only
270b57cec5SDimitry Andric /// returns something else if the result of toString would be ambiguous, e.g.
280b57cec5SDimitry Andric /// a string that parses as a number or boolean.
290b57cec5SDimitry Andric StringRef getYAMLTag() const;
300b57cec5SDimitry Andric };
310b57cec5SDimitry Andric
320b57cec5SDimitry Andric } // namespace
330b57cec5SDimitry Andric
340b57cec5SDimitry Andric /// Convert this DocNode to a string, assuming it is scalar.
toString() const350b57cec5SDimitry Andric std::string DocNode::toString() const {
360b57cec5SDimitry Andric std::string S;
370b57cec5SDimitry Andric raw_string_ostream OS(S);
380b57cec5SDimitry Andric switch (getKind()) {
390b57cec5SDimitry Andric case msgpack::Type::String:
400b57cec5SDimitry Andric OS << Raw;
410b57cec5SDimitry Andric break;
420b57cec5SDimitry Andric case msgpack::Type::Nil:
430b57cec5SDimitry Andric break;
440b57cec5SDimitry Andric case msgpack::Type::Boolean:
450b57cec5SDimitry Andric OS << (Bool ? "true" : "false");
460b57cec5SDimitry Andric break;
470b57cec5SDimitry Andric case msgpack::Type::Int:
480b57cec5SDimitry Andric OS << Int;
490b57cec5SDimitry Andric break;
500b57cec5SDimitry Andric case msgpack::Type::UInt:
510b57cec5SDimitry Andric if (getDocument()->getHexMode())
520b57cec5SDimitry Andric OS << format("%#llx", (unsigned long long)UInt);
530b57cec5SDimitry Andric else
540b57cec5SDimitry Andric OS << UInt;
550b57cec5SDimitry Andric break;
560b57cec5SDimitry Andric case msgpack::Type::Float:
570b57cec5SDimitry Andric OS << Float;
580b57cec5SDimitry Andric break;
590b57cec5SDimitry Andric default:
600b57cec5SDimitry Andric llvm_unreachable("not scalar");
610b57cec5SDimitry Andric break;
620b57cec5SDimitry Andric }
630b57cec5SDimitry Andric return OS.str();
640b57cec5SDimitry Andric }
650b57cec5SDimitry Andric
660b57cec5SDimitry Andric /// Convert the StringRef and use it to set this DocNode (assuming scalar). If
670b57cec5SDimitry Andric /// it is a string, copy the string into the Document's strings list so we do
680b57cec5SDimitry Andric /// not rely on S having a lifetime beyond this call. Tag is "" or a YAML tag.
fromString(StringRef S,StringRef Tag)690b57cec5SDimitry Andric StringRef DocNode::fromString(StringRef S, StringRef Tag) {
700b57cec5SDimitry Andric if (Tag == "tag:yaml.org,2002:str")
710b57cec5SDimitry Andric Tag = "";
720b57cec5SDimitry Andric if (Tag == "!int" || Tag == "") {
730b57cec5SDimitry Andric // Try unsigned int then signed int.
740b57cec5SDimitry Andric *this = getDocument()->getNode(uint64_t(0));
750b57cec5SDimitry Andric StringRef Err = yaml::ScalarTraits<uint64_t>::input(S, nullptr, getUInt());
760b57cec5SDimitry Andric if (Err != "") {
770b57cec5SDimitry Andric *this = getDocument()->getNode(int64_t(0));
780b57cec5SDimitry Andric Err = yaml::ScalarTraits<int64_t>::input(S, nullptr, getInt());
790b57cec5SDimitry Andric }
800b57cec5SDimitry Andric if (Err == "" || Tag != "")
810b57cec5SDimitry Andric return Err;
820b57cec5SDimitry Andric }
830b57cec5SDimitry Andric if (Tag == "!nil") {
840b57cec5SDimitry Andric *this = getDocument()->getNode();
850b57cec5SDimitry Andric return "";
860b57cec5SDimitry Andric }
870b57cec5SDimitry Andric if (Tag == "!bool" || Tag == "") {
880b57cec5SDimitry Andric *this = getDocument()->getNode(false);
890b57cec5SDimitry Andric StringRef Err = yaml::ScalarTraits<bool>::input(S, nullptr, getBool());
900b57cec5SDimitry Andric if (Err == "" || Tag != "")
910b57cec5SDimitry Andric return Err;
920b57cec5SDimitry Andric }
930b57cec5SDimitry Andric if (Tag == "!float" || Tag == "") {
940b57cec5SDimitry Andric *this = getDocument()->getNode(0.0);
950b57cec5SDimitry Andric StringRef Err = yaml::ScalarTraits<double>::input(S, nullptr, getFloat());
960b57cec5SDimitry Andric if (Err == "" || Tag != "")
970b57cec5SDimitry Andric return Err;
980b57cec5SDimitry Andric }
990b57cec5SDimitry Andric assert((Tag == "!str" || Tag == "") && "unsupported tag");
1000b57cec5SDimitry Andric std::string V;
1010b57cec5SDimitry Andric StringRef Err = yaml::ScalarTraits<std::string>::input(S, nullptr, V);
1020b57cec5SDimitry Andric if (Err == "")
1030b57cec5SDimitry Andric *this = getDocument()->getNode(V, /*Copy=*/true);
1040b57cec5SDimitry Andric return Err;
1050b57cec5SDimitry Andric }
1060b57cec5SDimitry Andric
1070b57cec5SDimitry Andric /// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only
1080b57cec5SDimitry Andric /// returns something else if the result of toString would be ambiguous, e.g.
1090b57cec5SDimitry Andric /// a string that parses as a number or boolean.
getYAMLTag() const1100b57cec5SDimitry Andric StringRef ScalarDocNode::getYAMLTag() const {
1110b57cec5SDimitry Andric if (getKind() == msgpack::Type::Nil)
1120b57cec5SDimitry Andric return "!nil";
1130b57cec5SDimitry Andric // Try converting both ways and see if we get the same kind. If not, we need
1140b57cec5SDimitry Andric // a tag.
1150b57cec5SDimitry Andric ScalarDocNode N = getDocument()->getNode();
1160b57cec5SDimitry Andric N.fromString(toString(), "");
1170b57cec5SDimitry Andric if (N.getKind() == getKind())
1180b57cec5SDimitry Andric return "";
1190b57cec5SDimitry Andric // Tolerate signedness of int changing, as tags do not differentiate between
1200b57cec5SDimitry Andric // them anyway.
1210b57cec5SDimitry Andric if (N.getKind() == msgpack::Type::UInt && getKind() == msgpack::Type::Int)
1220b57cec5SDimitry Andric return "";
1230b57cec5SDimitry Andric if (N.getKind() == msgpack::Type::Int && getKind() == msgpack::Type::UInt)
1240b57cec5SDimitry Andric return "";
1250b57cec5SDimitry Andric // We do need a tag.
1260b57cec5SDimitry Andric switch (getKind()) {
1270b57cec5SDimitry Andric case msgpack::Type::String:
1280b57cec5SDimitry Andric return "!str";
1290b57cec5SDimitry Andric case msgpack::Type::Int:
1300b57cec5SDimitry Andric return "!int";
1310b57cec5SDimitry Andric case msgpack::Type::UInt:
1320b57cec5SDimitry Andric return "!int";
1330b57cec5SDimitry Andric case msgpack::Type::Boolean:
1340b57cec5SDimitry Andric return "!bool";
1350b57cec5SDimitry Andric case msgpack::Type::Float:
1360b57cec5SDimitry Andric return "!float";
1370b57cec5SDimitry Andric default:
1380b57cec5SDimitry Andric llvm_unreachable("unrecognized kind");
1390b57cec5SDimitry Andric }
1400b57cec5SDimitry Andric }
1410b57cec5SDimitry Andric
1420b57cec5SDimitry Andric namespace llvm {
1430b57cec5SDimitry Andric namespace yaml {
1440b57cec5SDimitry Andric
1450b57cec5SDimitry Andric /// YAMLIO for DocNode
1460b57cec5SDimitry Andric template <> struct PolymorphicTraits<DocNode> {
1470b57cec5SDimitry Andric
getKindllvm::yaml::PolymorphicTraits1480b57cec5SDimitry Andric static NodeKind getKind(const DocNode &N) {
1490b57cec5SDimitry Andric switch (N.getKind()) {
1500b57cec5SDimitry Andric case msgpack::Type::Map:
1510b57cec5SDimitry Andric return NodeKind::Map;
1520b57cec5SDimitry Andric case msgpack::Type::Array:
1530b57cec5SDimitry Andric return NodeKind::Sequence;
1540b57cec5SDimitry Andric default:
1550b57cec5SDimitry Andric return NodeKind::Scalar;
1560b57cec5SDimitry Andric }
1570b57cec5SDimitry Andric }
1580b57cec5SDimitry Andric
getAsMapllvm::yaml::PolymorphicTraits1590b57cec5SDimitry Andric static MapDocNode &getAsMap(DocNode &N) { return N.getMap(/*Convert=*/true); }
1600b57cec5SDimitry Andric
getAsSequencellvm::yaml::PolymorphicTraits1610b57cec5SDimitry Andric static ArrayDocNode &getAsSequence(DocNode &N) {
1620b57cec5SDimitry Andric N.getArray(/*Convert=*/true);
1630b57cec5SDimitry Andric return *static_cast<ArrayDocNode *>(&N);
1640b57cec5SDimitry Andric }
1650b57cec5SDimitry Andric
getAsScalarllvm::yaml::PolymorphicTraits1660b57cec5SDimitry Andric static ScalarDocNode &getAsScalar(DocNode &N) {
1670b57cec5SDimitry Andric return *static_cast<ScalarDocNode *>(&N);
1680b57cec5SDimitry Andric }
1690b57cec5SDimitry Andric };
1700b57cec5SDimitry Andric
1710b57cec5SDimitry Andric /// YAMLIO for ScalarDocNode
1720b57cec5SDimitry Andric template <> struct TaggedScalarTraits<ScalarDocNode> {
1730b57cec5SDimitry Andric
outputllvm::yaml::TaggedScalarTraits1740b57cec5SDimitry Andric static void output(const ScalarDocNode &S, void *Ctxt, raw_ostream &OS,
1750b57cec5SDimitry Andric raw_ostream &TagOS) {
1760b57cec5SDimitry Andric TagOS << S.getYAMLTag();
1770b57cec5SDimitry Andric OS << S.toString();
1780b57cec5SDimitry Andric }
1790b57cec5SDimitry Andric
inputllvm::yaml::TaggedScalarTraits1800b57cec5SDimitry Andric static StringRef input(StringRef Str, StringRef Tag, void *Ctxt,
1810b57cec5SDimitry Andric ScalarDocNode &S) {
1820b57cec5SDimitry Andric return S.fromString(Str, Tag);
1830b57cec5SDimitry Andric }
1840b57cec5SDimitry Andric
mustQuotellvm::yaml::TaggedScalarTraits1850b57cec5SDimitry Andric static QuotingType mustQuote(const ScalarDocNode &S, StringRef ScalarStr) {
1860b57cec5SDimitry Andric switch (S.getKind()) {
1870b57cec5SDimitry Andric case Type::Int:
1880b57cec5SDimitry Andric return ScalarTraits<int64_t>::mustQuote(ScalarStr);
1890b57cec5SDimitry Andric case Type::UInt:
1900b57cec5SDimitry Andric return ScalarTraits<uint64_t>::mustQuote(ScalarStr);
1910b57cec5SDimitry Andric case Type::Nil:
1920b57cec5SDimitry Andric return ScalarTraits<StringRef>::mustQuote(ScalarStr);
1930b57cec5SDimitry Andric case Type::Boolean:
1940b57cec5SDimitry Andric return ScalarTraits<bool>::mustQuote(ScalarStr);
1950b57cec5SDimitry Andric case Type::Float:
1960b57cec5SDimitry Andric return ScalarTraits<double>::mustQuote(ScalarStr);
1970b57cec5SDimitry Andric case Type::Binary:
1980b57cec5SDimitry Andric case Type::String:
1990b57cec5SDimitry Andric return ScalarTraits<std::string>::mustQuote(ScalarStr);
2000b57cec5SDimitry Andric default:
2010b57cec5SDimitry Andric llvm_unreachable("unrecognized ScalarKind");
2020b57cec5SDimitry Andric }
2030b57cec5SDimitry Andric }
2040b57cec5SDimitry Andric };
2050b57cec5SDimitry Andric
2060b57cec5SDimitry Andric /// YAMLIO for MapDocNode
2070b57cec5SDimitry Andric template <> struct CustomMappingTraits<MapDocNode> {
2080b57cec5SDimitry Andric
inputOnellvm::yaml::CustomMappingTraits2090b57cec5SDimitry Andric static void inputOne(IO &IO, StringRef Key, MapDocNode &M) {
2100b57cec5SDimitry Andric ScalarDocNode KeyObj = M.getDocument()->getNode();
2110b57cec5SDimitry Andric KeyObj.fromString(Key, "");
2120b57cec5SDimitry Andric IO.mapRequired(Key.str().c_str(), M.getMap()[KeyObj]);
2130b57cec5SDimitry Andric }
2140b57cec5SDimitry Andric
outputllvm::yaml::CustomMappingTraits2150b57cec5SDimitry Andric static void output(IO &IO, MapDocNode &M) {
2160b57cec5SDimitry Andric for (auto I : M.getMap()) {
2170b57cec5SDimitry Andric IO.mapRequired(I.first.toString().c_str(), I.second);
2180b57cec5SDimitry Andric }
2190b57cec5SDimitry Andric }
2200b57cec5SDimitry Andric };
2210b57cec5SDimitry Andric
2220b57cec5SDimitry Andric /// YAMLIO for ArrayNode
2230b57cec5SDimitry Andric template <> struct SequenceTraits<ArrayDocNode> {
2240b57cec5SDimitry Andric
sizellvm::yaml::SequenceTraits2250b57cec5SDimitry Andric static size_t size(IO &IO, ArrayDocNode &A) { return A.size(); }
2260b57cec5SDimitry Andric
elementllvm::yaml::SequenceTraits2270b57cec5SDimitry Andric static DocNode &element(IO &IO, ArrayDocNode &A, size_t Index) {
2280b57cec5SDimitry Andric return A[Index];
2290b57cec5SDimitry Andric }
2300b57cec5SDimitry Andric };
2310b57cec5SDimitry Andric
2320b57cec5SDimitry Andric } // namespace yaml
2330b57cec5SDimitry Andric } // namespace llvm
2340b57cec5SDimitry Andric
2350b57cec5SDimitry Andric /// Convert MsgPack Document to YAML text.
toYAML(raw_ostream & OS)2360b57cec5SDimitry Andric void msgpack::Document::toYAML(raw_ostream &OS) {
2370b57cec5SDimitry Andric yaml::Output Yout(OS);
2380b57cec5SDimitry Andric Yout << getRoot();
2390b57cec5SDimitry Andric }
2400b57cec5SDimitry Andric
2410b57cec5SDimitry Andric /// Read YAML text into the MsgPack document. Returns false on failure.
fromYAML(StringRef S)2420b57cec5SDimitry Andric bool msgpack::Document::fromYAML(StringRef S) {
2430b57cec5SDimitry Andric clear();
2440b57cec5SDimitry Andric yaml::Input Yin(S);
2450b57cec5SDimitry Andric Yin >> getRoot();
2460b57cec5SDimitry Andric return !Yin.error();
2470b57cec5SDimitry Andric }
2480b57cec5SDimitry Andric
249