Home | History | Annotate | Line # | Download | only in BinaryFormat
      1 //===-- MsgPackDocumentYAML.cpp - MsgPack Document YAML interface -------*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 /// This file implements YAMLIO on a msgpack::Document.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "llvm/BinaryFormat/MsgPackDocument.h"
     15 #include "llvm/Support/YAMLTraits.h"
     16 
     17 using namespace llvm;
     18 using namespace msgpack;
     19 
     20 namespace {
     21 
     22 // Struct used to represent scalar node. (MapDocNode and ArrayDocNode already
     23 // exist in MsgPackDocument.h.)
     24 struct ScalarDocNode : DocNode {
     25   ScalarDocNode(DocNode N) : DocNode(N) {}
     26 
     27   /// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only
     28   /// returns something else if the result of toString would be ambiguous, e.g.
     29   /// a string that parses as a number or boolean.
     30   StringRef getYAMLTag() const;
     31 };
     32 
     33 } // namespace
     34 
     35 /// Convert this DocNode to a string, assuming it is scalar.
     36 std::string DocNode::toString() const {
     37   std::string S;
     38   raw_string_ostream OS(S);
     39   switch (getKind()) {
     40   case msgpack::Type::String:
     41     OS << Raw;
     42     break;
     43   case msgpack::Type::Nil:
     44     break;
     45   case msgpack::Type::Boolean:
     46     OS << (Bool ? "true" : "false");
     47     break;
     48   case msgpack::Type::Int:
     49     OS << Int;
     50     break;
     51   case msgpack::Type::UInt:
     52     if (getDocument()->getHexMode())
     53       OS << format("%#llx", (unsigned long long)UInt);
     54     else
     55       OS << UInt;
     56     break;
     57   case msgpack::Type::Float:
     58     OS << Float;
     59     break;
     60   default:
     61     llvm_unreachable("not scalar");
     62     break;
     63   }
     64   return OS.str();
     65 }
     66 
     67 /// Convert the StringRef and use it to set this DocNode (assuming scalar). If
     68 /// it is a string, copy the string into the Document's strings list so we do
     69 /// not rely on S having a lifetime beyond this call. Tag is "" or a YAML tag.
     70 StringRef DocNode::fromString(StringRef S, StringRef Tag) {
     71   if (Tag == "tag:yaml.org,2002:str")
     72     Tag = "";
     73   if (Tag == "!int" || Tag == "") {
     74     // Try unsigned int then signed int.
     75     *this = getDocument()->getNode(uint64_t(0));
     76     StringRef Err = yaml::ScalarTraits<uint64_t>::input(S, nullptr, getUInt());
     77     if (Err != "") {
     78       *this = getDocument()->getNode(int64_t(0));
     79       Err = yaml::ScalarTraits<int64_t>::input(S, nullptr, getInt());
     80     }
     81     if (Err == "" || Tag != "")
     82       return Err;
     83   }
     84   if (Tag == "!nil") {
     85     *this = getDocument()->getNode();
     86     return "";
     87   }
     88   if (Tag == "!bool" || Tag == "") {
     89     *this = getDocument()->getNode(false);
     90     StringRef Err = yaml::ScalarTraits<bool>::input(S, nullptr, getBool());
     91     if (Err == "" || Tag != "")
     92       return Err;
     93   }
     94   if (Tag == "!float" || Tag == "") {
     95     *this = getDocument()->getNode(0.0);
     96     StringRef Err = yaml::ScalarTraits<double>::input(S, nullptr, getFloat());
     97     if (Err == "" || Tag != "")
     98       return Err;
     99   }
    100   assert((Tag == "!str" || Tag == "") && "unsupported tag");
    101   std::string V;
    102   StringRef Err = yaml::ScalarTraits<std::string>::input(S, nullptr, V);
    103   if (Err == "")
    104     *this = getDocument()->getNode(V, /*Copy=*/true);
    105   return Err;
    106 }
    107 
    108 /// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only
    109 /// returns something else if the result of toString would be ambiguous, e.g.
    110 /// a string that parses as a number or boolean.
    111 StringRef ScalarDocNode::getYAMLTag() const {
    112   if (getKind() == msgpack::Type::Nil)
    113     return "!nil";
    114   // Try converting both ways and see if we get the same kind. If not, we need
    115   // a tag.
    116   ScalarDocNode N = getDocument()->getNode();
    117   N.fromString(toString(), "");
    118   if (N.getKind() == getKind())
    119     return "";
    120   // Tolerate signedness of int changing, as tags do not differentiate between
    121   // them anyway.
    122   if (N.getKind() == msgpack::Type::UInt && getKind() == msgpack::Type::Int)
    123     return "";
    124   if (N.getKind() == msgpack::Type::Int && getKind() == msgpack::Type::UInt)
    125     return "";
    126   // We do need a tag.
    127   switch (getKind()) {
    128   case msgpack::Type::String:
    129     return "!str";
    130   case msgpack::Type::Int:
    131     return "!int";
    132   case msgpack::Type::UInt:
    133     return "!int";
    134   case msgpack::Type::Boolean:
    135     return "!bool";
    136   case msgpack::Type::Float:
    137     return "!float";
    138   default:
    139     llvm_unreachable("unrecognized kind");
    140   }
    141 }
    142 
    143 namespace llvm {
    144 namespace yaml {
    145 
    146 /// YAMLIO for DocNode
    147 template <> struct PolymorphicTraits<DocNode> {
    148 
    149   static NodeKind getKind(const DocNode &N) {
    150     switch (N.getKind()) {
    151     case msgpack::Type::Map:
    152       return NodeKind::Map;
    153     case msgpack::Type::Array:
    154       return NodeKind::Sequence;
    155     default:
    156       return NodeKind::Scalar;
    157     }
    158   }
    159 
    160   static MapDocNode &getAsMap(DocNode &N) { return N.getMap(/*Convert=*/true); }
    161 
    162   static ArrayDocNode &getAsSequence(DocNode &N) {
    163     N.getArray(/*Convert=*/true);
    164     return *static_cast<ArrayDocNode *>(&N);
    165   }
    166 
    167   static ScalarDocNode &getAsScalar(DocNode &N) {
    168     return *static_cast<ScalarDocNode *>(&N);
    169   }
    170 };
    171 
    172 /// YAMLIO for ScalarDocNode
    173 template <> struct TaggedScalarTraits<ScalarDocNode> {
    174 
    175   static void output(const ScalarDocNode &S, void *Ctxt, raw_ostream &OS,
    176                      raw_ostream &TagOS) {
    177     TagOS << S.getYAMLTag();
    178     OS << S.toString();
    179   }
    180 
    181   static StringRef input(StringRef Str, StringRef Tag, void *Ctxt,
    182                          ScalarDocNode &S) {
    183     return S.fromString(Str, Tag);
    184   }
    185 
    186   static QuotingType mustQuote(const ScalarDocNode &S, StringRef ScalarStr) {
    187     switch (S.getKind()) {
    188     case Type::Int:
    189       return ScalarTraits<int64_t>::mustQuote(ScalarStr);
    190     case Type::UInt:
    191       return ScalarTraits<uint64_t>::mustQuote(ScalarStr);
    192     case Type::Nil:
    193       return ScalarTraits<StringRef>::mustQuote(ScalarStr);
    194     case Type::Boolean:
    195       return ScalarTraits<bool>::mustQuote(ScalarStr);
    196     case Type::Float:
    197       return ScalarTraits<double>::mustQuote(ScalarStr);
    198     case Type::Binary:
    199     case Type::String:
    200       return ScalarTraits<std::string>::mustQuote(ScalarStr);
    201     default:
    202       llvm_unreachable("unrecognized ScalarKind");
    203     }
    204   }
    205 };
    206 
    207 /// YAMLIO for MapDocNode
    208 template <> struct CustomMappingTraits<MapDocNode> {
    209 
    210   static void inputOne(IO &IO, StringRef Key, MapDocNode &M) {
    211     ScalarDocNode KeyObj = M.getDocument()->getNode();
    212     KeyObj.fromString(Key, "");
    213     IO.mapRequired(Key.str().c_str(), M.getMap()[KeyObj]);
    214   }
    215 
    216   static void output(IO &IO, MapDocNode &M) {
    217     for (auto I : M.getMap()) {
    218       IO.mapRequired(I.first.toString().c_str(), I.second);
    219     }
    220   }
    221 };
    222 
    223 /// YAMLIO for ArrayNode
    224 template <> struct SequenceTraits<ArrayDocNode> {
    225 
    226   static size_t size(IO &IO, ArrayDocNode &A) { return A.size(); }
    227 
    228   static DocNode &element(IO &IO, ArrayDocNode &A, size_t Index) {
    229     return A[Index];
    230   }
    231 };
    232 
    233 } // namespace yaml
    234 } // namespace llvm
    235 
    236 /// Convert MsgPack Document to YAML text.
    237 void msgpack::Document::toYAML(raw_ostream &OS) {
    238   yaml::Output Yout(OS);
    239   Yout << getRoot();
    240 }
    241 
    242 /// Read YAML text into the MsgPack document. Returns false on failure.
    243 bool msgpack::Document::fromYAML(StringRef S) {
    244   clear();
    245   yaml::Input Yin(S);
    246   Yin >> getRoot();
    247   return !Yin.error();
    248 }
    249 
    250