17330f729Sjoerg===================== 27330f729SjoergYAML I/O 37330f729Sjoerg===================== 47330f729Sjoerg 57330f729Sjoerg.. contents:: 67330f729Sjoerg :local: 77330f729Sjoerg 87330f729SjoergIntroduction to YAML 97330f729Sjoerg==================== 107330f729Sjoerg 117330f729SjoergYAML is a human readable data serialization language. The full YAML language 127330f729Sjoergspec can be read at `yaml.org 137330f729Sjoerg<http://www.yaml.org/spec/1.2/spec.html#Introduction>`_. The simplest form of 147330f729Sjoergyaml is just "scalars", "mappings", and "sequences". A scalar is any number 157330f729Sjoergor string. The pound/hash symbol (#) begins a comment line. A mapping is 167330f729Sjoerga set of key-value pairs where the key ends with a colon. For example: 177330f729Sjoerg 187330f729Sjoerg.. code-block:: yaml 197330f729Sjoerg 207330f729Sjoerg # a mapping 217330f729Sjoerg name: Tom 227330f729Sjoerg hat-size: 7 237330f729Sjoerg 247330f729SjoergA sequence is a list of items where each item starts with a leading dash ('-'). 257330f729SjoergFor example: 267330f729Sjoerg 277330f729Sjoerg.. code-block:: yaml 287330f729Sjoerg 297330f729Sjoerg # a sequence 307330f729Sjoerg - x86 317330f729Sjoerg - x86_64 327330f729Sjoerg - PowerPC 337330f729Sjoerg 347330f729SjoergYou can combine mappings and sequences by indenting. For example a sequence 357330f729Sjoergof mappings in which one of the mapping values is itself a sequence: 367330f729Sjoerg 377330f729Sjoerg.. code-block:: yaml 387330f729Sjoerg 397330f729Sjoerg # a sequence of mappings with one key's value being a sequence 407330f729Sjoerg - name: Tom 417330f729Sjoerg cpus: 427330f729Sjoerg - x86 437330f729Sjoerg - x86_64 447330f729Sjoerg - name: Bob 457330f729Sjoerg cpus: 467330f729Sjoerg - x86 477330f729Sjoerg - name: Dan 487330f729Sjoerg cpus: 497330f729Sjoerg - PowerPC 507330f729Sjoerg - x86 517330f729Sjoerg 527330f729SjoergSometime sequences are known to be short and the one entry per line is too 537330f729Sjoergverbose, so YAML offers an alternate syntax for sequences called a "Flow 547330f729SjoergSequence" in which you put comma separated sequence elements into square 557330f729Sjoergbrackets. The above example could then be simplified to : 567330f729Sjoerg 577330f729Sjoerg 587330f729Sjoerg.. code-block:: yaml 597330f729Sjoerg 607330f729Sjoerg # a sequence of mappings with one key's value being a flow sequence 617330f729Sjoerg - name: Tom 627330f729Sjoerg cpus: [ x86, x86_64 ] 637330f729Sjoerg - name: Bob 647330f729Sjoerg cpus: [ x86 ] 657330f729Sjoerg - name: Dan 667330f729Sjoerg cpus: [ PowerPC, x86 ] 677330f729Sjoerg 687330f729Sjoerg 697330f729SjoergIntroduction to YAML I/O 707330f729Sjoerg======================== 717330f729Sjoerg 727330f729SjoergThe use of indenting makes the YAML easy for a human to read and understand, 737330f729Sjoergbut having a program read and write YAML involves a lot of tedious details. 747330f729SjoergThe YAML I/O library structures and simplifies reading and writing YAML 757330f729Sjoergdocuments. 767330f729Sjoerg 777330f729SjoergYAML I/O assumes you have some "native" data structures which you want to be 787330f729Sjoergable to dump as YAML and recreate from YAML. The first step is to try 797330f729Sjoergwriting example YAML for your data structures. You may find after looking at 807330f729Sjoergpossible YAML representations that a direct mapping of your data structures 817330f729Sjoergto YAML is not very readable. Often the fields are not in the order that 827330f729Sjoerga human would find readable. Or the same information is replicated in multiple 837330f729Sjoerglocations, making it hard for a human to write such YAML correctly. 847330f729Sjoerg 857330f729SjoergIn relational database theory there is a design step called normalization in 867330f729Sjoergwhich you reorganize fields and tables. The same considerations need to 877330f729Sjoerggo into the design of your YAML encoding. But, you may not want to change 887330f729Sjoergyour existing native data structures. Therefore, when writing out YAML 897330f729Sjoergthere may be a normalization step, and when reading YAML there would be a 907330f729Sjoergcorresponding denormalization step. 917330f729Sjoerg 927330f729SjoergYAML I/O uses a non-invasive, traits based design. YAML I/O defines some 937330f729Sjoergabstract base templates. You specialize those templates on your data types. 947330f729SjoergFor instance, if you have an enumerated type FooBar you could specialize 957330f729SjoergScalarEnumerationTraits on that type and define the enumeration() method: 967330f729Sjoerg 977330f729Sjoerg.. code-block:: c++ 987330f729Sjoerg 997330f729Sjoerg using llvm::yaml::ScalarEnumerationTraits; 1007330f729Sjoerg using llvm::yaml::IO; 1017330f729Sjoerg 1027330f729Sjoerg template <> 1037330f729Sjoerg struct ScalarEnumerationTraits<FooBar> { 1047330f729Sjoerg static void enumeration(IO &io, FooBar &value) { 1057330f729Sjoerg ... 1067330f729Sjoerg } 1077330f729Sjoerg }; 1087330f729Sjoerg 1097330f729Sjoerg 1107330f729SjoergAs with all YAML I/O template specializations, the ScalarEnumerationTraits is used for 1117330f729Sjoergboth reading and writing YAML. That is, the mapping between in-memory enum 1127330f729Sjoergvalues and the YAML string representation is only in one place. 1137330f729SjoergThis assures that the code for writing and parsing of YAML stays in sync. 1147330f729Sjoerg 1157330f729SjoergTo specify a YAML mappings, you define a specialization on 1167330f729Sjoergllvm::yaml::MappingTraits. 1177330f729SjoergIf your native data structure happens to be a struct that is already normalized, 1187330f729Sjoergthen the specialization is simple. For example: 1197330f729Sjoerg 1207330f729Sjoerg.. code-block:: c++ 1217330f729Sjoerg 1227330f729Sjoerg using llvm::yaml::MappingTraits; 1237330f729Sjoerg using llvm::yaml::IO; 1247330f729Sjoerg 1257330f729Sjoerg template <> 1267330f729Sjoerg struct MappingTraits<Person> { 1277330f729Sjoerg static void mapping(IO &io, Person &info) { 1287330f729Sjoerg io.mapRequired("name", info.name); 1297330f729Sjoerg io.mapOptional("hat-size", info.hatSize); 1307330f729Sjoerg } 1317330f729Sjoerg }; 1327330f729Sjoerg 1337330f729Sjoerg 1347330f729SjoergA YAML sequence is automatically inferred if you data type has begin()/end() 1357330f729Sjoergiterators and a push_back() method. Therefore any of the STL containers 1367330f729Sjoerg(such as std::vector<>) will automatically translate to YAML sequences. 1377330f729Sjoerg 1387330f729SjoergOnce you have defined specializations for your data types, you can 1397330f729Sjoergprogrammatically use YAML I/O to write a YAML document: 1407330f729Sjoerg 1417330f729Sjoerg.. code-block:: c++ 1427330f729Sjoerg 1437330f729Sjoerg using llvm::yaml::Output; 1447330f729Sjoerg 1457330f729Sjoerg Person tom; 1467330f729Sjoerg tom.name = "Tom"; 1477330f729Sjoerg tom.hatSize = 8; 1487330f729Sjoerg Person dan; 1497330f729Sjoerg dan.name = "Dan"; 1507330f729Sjoerg dan.hatSize = 7; 1517330f729Sjoerg std::vector<Person> persons; 1527330f729Sjoerg persons.push_back(tom); 1537330f729Sjoerg persons.push_back(dan); 1547330f729Sjoerg 1557330f729Sjoerg Output yout(llvm::outs()); 1567330f729Sjoerg yout << persons; 1577330f729Sjoerg 1587330f729SjoergThis would write the following: 1597330f729Sjoerg 1607330f729Sjoerg.. code-block:: yaml 1617330f729Sjoerg 1627330f729Sjoerg - name: Tom 1637330f729Sjoerg hat-size: 8 1647330f729Sjoerg - name: Dan 1657330f729Sjoerg hat-size: 7 1667330f729Sjoerg 1677330f729SjoergAnd you can also read such YAML documents with the following code: 1687330f729Sjoerg 1697330f729Sjoerg.. code-block:: c++ 1707330f729Sjoerg 1717330f729Sjoerg using llvm::yaml::Input; 1727330f729Sjoerg 1737330f729Sjoerg typedef std::vector<Person> PersonList; 1747330f729Sjoerg std::vector<PersonList> docs; 1757330f729Sjoerg 1767330f729Sjoerg Input yin(document.getBuffer()); 1777330f729Sjoerg yin >> docs; 1787330f729Sjoerg 1797330f729Sjoerg if ( yin.error() ) 1807330f729Sjoerg return; 1817330f729Sjoerg 1827330f729Sjoerg // Process read document 1837330f729Sjoerg for ( PersonList &pl : docs ) { 1847330f729Sjoerg for ( Person &person : pl ) { 1857330f729Sjoerg cout << "name=" << person.name; 1867330f729Sjoerg } 1877330f729Sjoerg } 1887330f729Sjoerg 1897330f729SjoergOne other feature of YAML is the ability to define multiple documents in a 1907330f729Sjoergsingle file. That is why reading YAML produces a vector of your document type. 1917330f729Sjoerg 1927330f729Sjoerg 1937330f729Sjoerg 1947330f729SjoergError Handling 1957330f729Sjoerg============== 1967330f729Sjoerg 1977330f729SjoergWhen parsing a YAML document, if the input does not match your schema (as 1987330f729Sjoergexpressed in your XxxTraits<> specializations). YAML I/O 1997330f729Sjoergwill print out an error message and your Input object's error() method will 2007330f729Sjoergreturn true. For instance the following document: 2017330f729Sjoerg 2027330f729Sjoerg.. code-block:: yaml 2037330f729Sjoerg 2047330f729Sjoerg - name: Tom 2057330f729Sjoerg shoe-size: 12 2067330f729Sjoerg - name: Dan 2077330f729Sjoerg hat-size: 7 2087330f729Sjoerg 2097330f729SjoergHas a key (shoe-size) that is not defined in the schema. YAML I/O will 2107330f729Sjoergautomatically generate this error: 2117330f729Sjoerg 2127330f729Sjoerg.. code-block:: yaml 2137330f729Sjoerg 2147330f729Sjoerg YAML:2:2: error: unknown key 'shoe-size' 2157330f729Sjoerg shoe-size: 12 2167330f729Sjoerg ^~~~~~~~~ 2177330f729Sjoerg 2187330f729SjoergSimilar errors are produced for other input not conforming to the schema. 2197330f729Sjoerg 2207330f729Sjoerg 2217330f729SjoergScalars 2227330f729Sjoerg======= 2237330f729Sjoerg 2247330f729SjoergYAML scalars are just strings (i.e. not a sequence or mapping). The YAML I/O 2257330f729Sjoerglibrary provides support for translating between YAML scalars and specific 2267330f729SjoergC++ types. 2277330f729Sjoerg 2287330f729Sjoerg 2297330f729SjoergBuilt-in types 2307330f729Sjoerg-------------- 2317330f729SjoergThe following types have built-in support in YAML I/O: 2327330f729Sjoerg 2337330f729Sjoerg* bool 2347330f729Sjoerg* float 2357330f729Sjoerg* double 2367330f729Sjoerg* StringRef 2377330f729Sjoerg* std::string 2387330f729Sjoerg* int64_t 2397330f729Sjoerg* int32_t 2407330f729Sjoerg* int16_t 2417330f729Sjoerg* int8_t 2427330f729Sjoerg* uint64_t 2437330f729Sjoerg* uint32_t 2447330f729Sjoerg* uint16_t 2457330f729Sjoerg* uint8_t 2467330f729Sjoerg 2477330f729SjoergThat is, you can use those types in fields of MappingTraits or as element type 2487330f729Sjoergin sequence. When reading, YAML I/O will validate that the string found 2497330f729Sjoergis convertible to that type and error out if not. 2507330f729Sjoerg 2517330f729Sjoerg 2527330f729SjoergUnique types 2537330f729Sjoerg------------ 2547330f729SjoergGiven that YAML I/O is trait based, the selection of how to convert your data 2557330f729Sjoergto YAML is based on the type of your data. But in C++ type matching, typedefs 2567330f729Sjoergdo not generate unique type names. That means if you have two typedefs of 2577330f729Sjoergunsigned int, to YAML I/O both types look exactly like unsigned int. To 2587330f729Sjoergfacilitate make unique type names, YAML I/O provides a macro which is used 2597330f729Sjoerglike a typedef on built-in types, but expands to create a class with conversion 2607330f729Sjoergoperators to and from the base type. For example: 2617330f729Sjoerg 2627330f729Sjoerg.. code-block:: c++ 2637330f729Sjoerg 2647330f729Sjoerg LLVM_YAML_STRONG_TYPEDEF(uint32_t, MyFooFlags) 2657330f729Sjoerg LLVM_YAML_STRONG_TYPEDEF(uint32_t, MyBarFlags) 2667330f729Sjoerg 2677330f729SjoergThis generates two classes MyFooFlags and MyBarFlags which you can use in your 2687330f729Sjoergnative data structures instead of uint32_t. They are implicitly 2697330f729Sjoergconverted to and from uint32_t. The point of creating these unique types 2707330f729Sjoergis that you can now specify traits on them to get different YAML conversions. 2717330f729Sjoerg 2727330f729SjoergHex types 2737330f729Sjoerg--------- 2747330f729SjoergAn example use of a unique type is that YAML I/O provides fixed sized unsigned 2757330f729Sjoergintegers that are written with YAML I/O as hexadecimal instead of the decimal 2767330f729Sjoergformat used by the built-in integer types: 2777330f729Sjoerg 2787330f729Sjoerg* Hex64 2797330f729Sjoerg* Hex32 2807330f729Sjoerg* Hex16 2817330f729Sjoerg* Hex8 2827330f729Sjoerg 2837330f729SjoergYou can use llvm::yaml::Hex32 instead of uint32_t and the only different will 2847330f729Sjoergbe that when YAML I/O writes out that type it will be formatted in hexadecimal. 2857330f729Sjoerg 2867330f729Sjoerg 2877330f729SjoergScalarEnumerationTraits 2887330f729Sjoerg----------------------- 2897330f729SjoergYAML I/O supports translating between in-memory enumerations and a set of string 2907330f729Sjoergvalues in YAML documents. This is done by specializing ScalarEnumerationTraits<> 291*82d56013Sjoergon your enumeration type and define an enumeration() method. 2927330f729SjoergFor instance, suppose you had an enumeration of CPUs and a struct with it as 2937330f729Sjoerga field: 2947330f729Sjoerg 2957330f729Sjoerg.. code-block:: c++ 2967330f729Sjoerg 2977330f729Sjoerg enum CPUs { 2987330f729Sjoerg cpu_x86_64 = 5, 2997330f729Sjoerg cpu_x86 = 7, 3007330f729Sjoerg cpu_PowerPC = 8 3017330f729Sjoerg }; 3027330f729Sjoerg 3037330f729Sjoerg struct Info { 3047330f729Sjoerg CPUs cpu; 3057330f729Sjoerg uint32_t flags; 3067330f729Sjoerg }; 3077330f729Sjoerg 3087330f729SjoergTo support reading and writing of this enumeration, you can define a 3097330f729SjoergScalarEnumerationTraits specialization on CPUs, which can then be used 3107330f729Sjoergas a field type: 3117330f729Sjoerg 3127330f729Sjoerg.. code-block:: c++ 3137330f729Sjoerg 3147330f729Sjoerg using llvm::yaml::ScalarEnumerationTraits; 3157330f729Sjoerg using llvm::yaml::MappingTraits; 3167330f729Sjoerg using llvm::yaml::IO; 3177330f729Sjoerg 3187330f729Sjoerg template <> 3197330f729Sjoerg struct ScalarEnumerationTraits<CPUs> { 3207330f729Sjoerg static void enumeration(IO &io, CPUs &value) { 3217330f729Sjoerg io.enumCase(value, "x86_64", cpu_x86_64); 3227330f729Sjoerg io.enumCase(value, "x86", cpu_x86); 3237330f729Sjoerg io.enumCase(value, "PowerPC", cpu_PowerPC); 3247330f729Sjoerg } 3257330f729Sjoerg }; 3267330f729Sjoerg 3277330f729Sjoerg template <> 3287330f729Sjoerg struct MappingTraits<Info> { 3297330f729Sjoerg static void mapping(IO &io, Info &info) { 3307330f729Sjoerg io.mapRequired("cpu", info.cpu); 3317330f729Sjoerg io.mapOptional("flags", info.flags, 0); 3327330f729Sjoerg } 3337330f729Sjoerg }; 3347330f729Sjoerg 3357330f729SjoergWhen reading YAML, if the string found does not match any of the strings 3367330f729Sjoergspecified by enumCase() methods, an error is automatically generated. 3377330f729SjoergWhen writing YAML, if the value being written does not match any of the values 3387330f729Sjoergspecified by the enumCase() methods, a runtime assertion is triggered. 3397330f729Sjoerg 3407330f729Sjoerg 3417330f729SjoergBitValue 3427330f729Sjoerg-------- 3437330f729SjoergAnother common data structure in C++ is a field where each bit has a unique 3447330f729Sjoergmeaning. This is often used in a "flags" field. YAML I/O has support for 3457330f729Sjoergconverting such fields to a flow sequence. For instance suppose you 3467330f729Sjoerghad the following bit flags defined: 3477330f729Sjoerg 3487330f729Sjoerg.. code-block:: c++ 3497330f729Sjoerg 3507330f729Sjoerg enum { 3517330f729Sjoerg flagsPointy = 1 3527330f729Sjoerg flagsHollow = 2 3537330f729Sjoerg flagsFlat = 4 3547330f729Sjoerg flagsRound = 8 3557330f729Sjoerg }; 3567330f729Sjoerg 3577330f729Sjoerg LLVM_YAML_STRONG_TYPEDEF(uint32_t, MyFlags) 3587330f729Sjoerg 3597330f729SjoergTo support reading and writing of MyFlags, you specialize ScalarBitSetTraits<> 3607330f729Sjoergon MyFlags and provide the bit values and their names. 3617330f729Sjoerg 3627330f729Sjoerg.. code-block:: c++ 3637330f729Sjoerg 3647330f729Sjoerg using llvm::yaml::ScalarBitSetTraits; 3657330f729Sjoerg using llvm::yaml::MappingTraits; 3667330f729Sjoerg using llvm::yaml::IO; 3677330f729Sjoerg 3687330f729Sjoerg template <> 3697330f729Sjoerg struct ScalarBitSetTraits<MyFlags> { 3707330f729Sjoerg static void bitset(IO &io, MyFlags &value) { 3717330f729Sjoerg io.bitSetCase(value, "hollow", flagHollow); 3727330f729Sjoerg io.bitSetCase(value, "flat", flagFlat); 3737330f729Sjoerg io.bitSetCase(value, "round", flagRound); 3747330f729Sjoerg io.bitSetCase(value, "pointy", flagPointy); 3757330f729Sjoerg } 3767330f729Sjoerg }; 3777330f729Sjoerg 3787330f729Sjoerg struct Info { 3797330f729Sjoerg StringRef name; 3807330f729Sjoerg MyFlags flags; 3817330f729Sjoerg }; 3827330f729Sjoerg 3837330f729Sjoerg template <> 3847330f729Sjoerg struct MappingTraits<Info> { 3857330f729Sjoerg static void mapping(IO &io, Info& info) { 3867330f729Sjoerg io.mapRequired("name", info.name); 3877330f729Sjoerg io.mapRequired("flags", info.flags); 3887330f729Sjoerg } 3897330f729Sjoerg }; 3907330f729Sjoerg 3917330f729SjoergWith the above, YAML I/O (when writing) will test mask each value in the 3927330f729Sjoergbitset trait against the flags field, and each that matches will 3937330f729Sjoergcause the corresponding string to be added to the flow sequence. The opposite 394*82d56013Sjoergis done when reading and any unknown string values will result in an error. With 3957330f729Sjoergthe above schema, a same valid YAML document is: 3967330f729Sjoerg 3977330f729Sjoerg.. code-block:: yaml 3987330f729Sjoerg 3997330f729Sjoerg name: Tom 4007330f729Sjoerg flags: [ pointy, flat ] 4017330f729Sjoerg 4027330f729SjoergSometimes a "flags" field might contains an enumeration part 4037330f729Sjoergdefined by a bit-mask. 4047330f729Sjoerg 4057330f729Sjoerg.. code-block:: c++ 4067330f729Sjoerg 4077330f729Sjoerg enum { 4087330f729Sjoerg flagsFeatureA = 1, 4097330f729Sjoerg flagsFeatureB = 2, 4107330f729Sjoerg flagsFeatureC = 4, 4117330f729Sjoerg 4127330f729Sjoerg flagsCPUMask = 24, 4137330f729Sjoerg 4147330f729Sjoerg flagsCPU1 = 8, 4157330f729Sjoerg flagsCPU2 = 16 4167330f729Sjoerg }; 4177330f729Sjoerg 4187330f729SjoergTo support reading and writing such fields, you need to use the maskedBitSet() 4197330f729Sjoergmethod and provide the bit values, their names and the enumeration mask. 4207330f729Sjoerg 4217330f729Sjoerg.. code-block:: c++ 4227330f729Sjoerg 4237330f729Sjoerg template <> 4247330f729Sjoerg struct ScalarBitSetTraits<MyFlags> { 4257330f729Sjoerg static void bitset(IO &io, MyFlags &value) { 4267330f729Sjoerg io.bitSetCase(value, "featureA", flagsFeatureA); 4277330f729Sjoerg io.bitSetCase(value, "featureB", flagsFeatureB); 4287330f729Sjoerg io.bitSetCase(value, "featureC", flagsFeatureC); 4297330f729Sjoerg io.maskedBitSetCase(value, "CPU1", flagsCPU1, flagsCPUMask); 4307330f729Sjoerg io.maskedBitSetCase(value, "CPU2", flagsCPU2, flagsCPUMask); 4317330f729Sjoerg } 4327330f729Sjoerg }; 4337330f729Sjoerg 4347330f729SjoergYAML I/O (when writing) will apply the enumeration mask to the flags field, 4357330f729Sjoergand compare the result and values from the bitset. As in case of a regular 4367330f729Sjoergbitset, each that matches will cause the corresponding string to be added 4377330f729Sjoergto the flow sequence. 4387330f729Sjoerg 4397330f729SjoergCustom Scalar 4407330f729Sjoerg------------- 4417330f729SjoergSometimes for readability a scalar needs to be formatted in a custom way. For 442*82d56013Sjoerginstance your internal data structure may use an integer for time (seconds since 4437330f729Sjoergsome epoch), but in YAML it would be much nicer to express that integer in 4447330f729Sjoergsome time format (e.g. 4-May-2012 10:30pm). YAML I/O has a way to support 4457330f729Sjoergcustom formatting and parsing of scalar types by specializing ScalarTraits<> on 4467330f729Sjoergyour data type. When writing, YAML I/O will provide the native type and 4477330f729Sjoergyour specialization must create a temporary llvm::StringRef. When reading, 4487330f729SjoergYAML I/O will provide an llvm::StringRef of scalar and your specialization 4497330f729Sjoergmust convert that to your native data type. An outline of a custom scalar type 4507330f729Sjoerglooks like: 4517330f729Sjoerg 4527330f729Sjoerg.. code-block:: c++ 4537330f729Sjoerg 4547330f729Sjoerg using llvm::yaml::ScalarTraits; 4557330f729Sjoerg using llvm::yaml::IO; 4567330f729Sjoerg 4577330f729Sjoerg template <> 4587330f729Sjoerg struct ScalarTraits<MyCustomType> { 4597330f729Sjoerg static void output(const MyCustomType &value, void*, 4607330f729Sjoerg llvm::raw_ostream &out) { 4617330f729Sjoerg out << value; // do custom formatting here 4627330f729Sjoerg } 4637330f729Sjoerg static StringRef input(StringRef scalar, void*, MyCustomType &value) { 4647330f729Sjoerg // do custom parsing here. Return the empty string on success, 4657330f729Sjoerg // or an error message on failure. 4667330f729Sjoerg return StringRef(); 4677330f729Sjoerg } 4687330f729Sjoerg // Determine if this scalar needs quotes. 4697330f729Sjoerg static QuotingType mustQuote(StringRef) { return QuotingType::Single; } 4707330f729Sjoerg }; 4717330f729Sjoerg 4727330f729SjoergBlock Scalars 4737330f729Sjoerg------------- 4747330f729Sjoerg 4757330f729SjoergYAML block scalars are string literals that are represented in YAML using the 4767330f729Sjoergliteral block notation, just like the example shown below: 4777330f729Sjoerg 4787330f729Sjoerg.. code-block:: yaml 4797330f729Sjoerg 4807330f729Sjoerg text: | 4817330f729Sjoerg First line 4827330f729Sjoerg Second line 4837330f729Sjoerg 4847330f729SjoergThe YAML I/O library provides support for translating between YAML block scalars 4857330f729Sjoergand specific C++ types by allowing you to specialize BlockScalarTraits<> on 4867330f729Sjoergyour data type. The library doesn't provide any built-in support for block 4877330f729Sjoergscalar I/O for types like std::string and llvm::StringRef as they are already 4887330f729Sjoergsupported by YAML I/O and use the ordinary scalar notation by default. 4897330f729Sjoerg 4907330f729SjoergBlockScalarTraits specializations are very similar to the 4917330f729SjoergScalarTraits specialization - YAML I/O will provide the native type and your 4927330f729Sjoergspecialization must create a temporary llvm::StringRef when writing, and 4937330f729Sjoergit will also provide an llvm::StringRef that has the value of that block scalar 4947330f729Sjoergand your specialization must convert that to your native data type when reading. 4957330f729SjoergAn example of a custom type with an appropriate specialization of 4967330f729SjoergBlockScalarTraits is shown below: 4977330f729Sjoerg 4987330f729Sjoerg.. code-block:: c++ 4997330f729Sjoerg 5007330f729Sjoerg using llvm::yaml::BlockScalarTraits; 5017330f729Sjoerg using llvm::yaml::IO; 5027330f729Sjoerg 5037330f729Sjoerg struct MyStringType { 5047330f729Sjoerg std::string Str; 5057330f729Sjoerg }; 5067330f729Sjoerg 5077330f729Sjoerg template <> 5087330f729Sjoerg struct BlockScalarTraits<MyStringType> { 5097330f729Sjoerg static void output(const MyStringType &Value, void *Ctxt, 5107330f729Sjoerg llvm::raw_ostream &OS) { 5117330f729Sjoerg OS << Value.Str; 5127330f729Sjoerg } 5137330f729Sjoerg 5147330f729Sjoerg static StringRef input(StringRef Scalar, void *Ctxt, 5157330f729Sjoerg MyStringType &Value) { 5167330f729Sjoerg Value.Str = Scalar.str(); 5177330f729Sjoerg return StringRef(); 5187330f729Sjoerg } 5197330f729Sjoerg }; 5207330f729Sjoerg 5217330f729Sjoerg 5227330f729Sjoerg 5237330f729SjoergMappings 5247330f729Sjoerg======== 5257330f729Sjoerg 5267330f729SjoergTo be translated to or from a YAML mapping for your type T you must specialize 5277330f729Sjoergllvm::yaml::MappingTraits on T and implement the "void mapping(IO &io, T&)" 5287330f729Sjoergmethod. If your native data structures use pointers to a class everywhere, 5297330f729Sjoergyou can specialize on the class pointer. Examples: 5307330f729Sjoerg 5317330f729Sjoerg.. code-block:: c++ 5327330f729Sjoerg 5337330f729Sjoerg using llvm::yaml::MappingTraits; 5347330f729Sjoerg using llvm::yaml::IO; 5357330f729Sjoerg 5367330f729Sjoerg // Example of struct Foo which is used by value 5377330f729Sjoerg template <> 5387330f729Sjoerg struct MappingTraits<Foo> { 5397330f729Sjoerg static void mapping(IO &io, Foo &foo) { 5407330f729Sjoerg io.mapOptional("size", foo.size); 5417330f729Sjoerg ... 5427330f729Sjoerg } 5437330f729Sjoerg }; 5447330f729Sjoerg 5457330f729Sjoerg // Example of struct Bar which is natively always a pointer 5467330f729Sjoerg template <> 5477330f729Sjoerg struct MappingTraits<Bar*> { 5487330f729Sjoerg static void mapping(IO &io, Bar *&bar) { 5497330f729Sjoerg io.mapOptional("size", bar->size); 5507330f729Sjoerg ... 5517330f729Sjoerg } 5527330f729Sjoerg }; 5537330f729Sjoerg 5547330f729Sjoerg 5557330f729SjoergNo Normalization 5567330f729Sjoerg---------------- 5577330f729Sjoerg 5587330f729SjoergThe mapping() method is responsible, if needed, for normalizing and 5597330f729Sjoergdenormalizing. In a simple case where the native data structure requires no 5607330f729Sjoergnormalization, the mapping method just uses mapOptional() or mapRequired() to 5617330f729Sjoergbind the struct's fields to YAML key names. For example: 5627330f729Sjoerg 5637330f729Sjoerg.. code-block:: c++ 5647330f729Sjoerg 5657330f729Sjoerg using llvm::yaml::MappingTraits; 5667330f729Sjoerg using llvm::yaml::IO; 5677330f729Sjoerg 5687330f729Sjoerg template <> 5697330f729Sjoerg struct MappingTraits<Person> { 5707330f729Sjoerg static void mapping(IO &io, Person &info) { 5717330f729Sjoerg io.mapRequired("name", info.name); 5727330f729Sjoerg io.mapOptional("hat-size", info.hatSize); 5737330f729Sjoerg } 5747330f729Sjoerg }; 5757330f729Sjoerg 5767330f729Sjoerg 5777330f729SjoergNormalization 5787330f729Sjoerg---------------- 5797330f729Sjoerg 5807330f729SjoergWhen [de]normalization is required, the mapping() method needs a way to access 5817330f729Sjoergnormalized values as fields. To help with this, there is 5827330f729Sjoerga template MappingNormalization<> which you can then use to automatically 5837330f729Sjoergdo the normalization and denormalization. The template is used to create 5847330f729Sjoerga local variable in your mapping() method which contains the normalized keys. 5857330f729Sjoerg 5867330f729SjoergSuppose you have native data type 5877330f729SjoergPolar which specifies a position in polar coordinates (distance, angle): 5887330f729Sjoerg 5897330f729Sjoerg.. code-block:: c++ 5907330f729Sjoerg 5917330f729Sjoerg struct Polar { 5927330f729Sjoerg float distance; 5937330f729Sjoerg float angle; 5947330f729Sjoerg }; 5957330f729Sjoerg 5967330f729Sjoergbut you've decided the normalized YAML for should be in x,y coordinates. That 5977330f729Sjoergis, you want the yaml to look like: 5987330f729Sjoerg 5997330f729Sjoerg.. code-block:: yaml 6007330f729Sjoerg 6017330f729Sjoerg x: 10.3 6027330f729Sjoerg y: -4.7 6037330f729Sjoerg 6047330f729SjoergYou can support this by defining a MappingTraits that normalizes the polar 6057330f729Sjoergcoordinates to x,y coordinates when writing YAML and denormalizes x,y 6067330f729Sjoergcoordinates into polar when reading YAML. 6077330f729Sjoerg 6087330f729Sjoerg.. code-block:: c++ 6097330f729Sjoerg 6107330f729Sjoerg using llvm::yaml::MappingTraits; 6117330f729Sjoerg using llvm::yaml::IO; 6127330f729Sjoerg 6137330f729Sjoerg template <> 6147330f729Sjoerg struct MappingTraits<Polar> { 6157330f729Sjoerg 6167330f729Sjoerg class NormalizedPolar { 6177330f729Sjoerg public: 6187330f729Sjoerg NormalizedPolar(IO &io) 6197330f729Sjoerg : x(0.0), y(0.0) { 6207330f729Sjoerg } 6217330f729Sjoerg NormalizedPolar(IO &, Polar &polar) 6227330f729Sjoerg : x(polar.distance * cos(polar.angle)), 6237330f729Sjoerg y(polar.distance * sin(polar.angle)) { 6247330f729Sjoerg } 6257330f729Sjoerg Polar denormalize(IO &) { 6267330f729Sjoerg return Polar(sqrt(x*x+y*y), arctan(x,y)); 6277330f729Sjoerg } 6287330f729Sjoerg 6297330f729Sjoerg float x; 6307330f729Sjoerg float y; 6317330f729Sjoerg }; 6327330f729Sjoerg 6337330f729Sjoerg static void mapping(IO &io, Polar &polar) { 6347330f729Sjoerg MappingNormalization<NormalizedPolar, Polar> keys(io, polar); 6357330f729Sjoerg 6367330f729Sjoerg io.mapRequired("x", keys->x); 6377330f729Sjoerg io.mapRequired("y", keys->y); 6387330f729Sjoerg } 6397330f729Sjoerg }; 6407330f729Sjoerg 6417330f729SjoergWhen writing YAML, the local variable "keys" will be a stack allocated 6427330f729Sjoerginstance of NormalizedPolar, constructed from the supplied polar object which 6437330f729Sjoerginitializes it x and y fields. The mapRequired() methods then write out the x 6447330f729Sjoergand y values as key/value pairs. 6457330f729Sjoerg 6467330f729SjoergWhen reading YAML, the local variable "keys" will be a stack allocated instance 6477330f729Sjoergof NormalizedPolar, constructed by the empty constructor. The mapRequired 6487330f729Sjoergmethods will find the matching key in the YAML document and fill in the x and y 6497330f729Sjoergfields of the NormalizedPolar object keys. At the end of the mapping() method 6507330f729Sjoergwhen the local keys variable goes out of scope, the denormalize() method will 6517330f729Sjoergautomatically be called to convert the read values back to polar coordinates, 6527330f729Sjoergand then assigned back to the second parameter to mapping(). 6537330f729Sjoerg 6547330f729SjoergIn some cases, the normalized class may be a subclass of the native type and 6557330f729Sjoergcould be returned by the denormalize() method, except that the temporary 6567330f729Sjoergnormalized instance is stack allocated. In these cases, the utility template 6577330f729SjoergMappingNormalizationHeap<> can be used instead. It just like 6587330f729SjoergMappingNormalization<> except that it heap allocates the normalized object 6597330f729Sjoergwhen reading YAML. It never destroys the normalized object. The denormalize() 6607330f729Sjoergmethod can this return "this". 6617330f729Sjoerg 6627330f729Sjoerg 6637330f729SjoergDefault values 6647330f729Sjoerg-------------- 6657330f729SjoergWithin a mapping() method, calls to io.mapRequired() mean that that key is 6667330f729Sjoergrequired to exist when parsing YAML documents, otherwise YAML I/O will issue an 6677330f729Sjoergerror. 6687330f729Sjoerg 6697330f729SjoergOn the other hand, keys registered with io.mapOptional() are allowed to not 6707330f729Sjoergexist in the YAML document being read. So what value is put in the field 6717330f729Sjoergfor those optional keys? 6727330f729SjoergThere are two steps to how those optional fields are filled in. First, the 6737330f729Sjoergsecond parameter to the mapping() method is a reference to a native class. That 6747330f729Sjoergnative class must have a default constructor. Whatever value the default 6757330f729Sjoergconstructor initially sets for an optional field will be that field's value. 6767330f729SjoergSecond, the mapOptional() method has an optional third parameter. If provided 6777330f729Sjoergit is the value that mapOptional() should set that field to if the YAML document 6787330f729Sjoergdoes not have that key. 6797330f729Sjoerg 6807330f729SjoergThere is one important difference between those two ways (default constructor 6817330f729Sjoergand third parameter to mapOptional). When YAML I/O generates a YAML document, 6827330f729Sjoergif the mapOptional() third parameter is used, if the actual value being written 6837330f729Sjoergis the same as (using ==) the default value, then that key/value is not written. 6847330f729Sjoerg 6857330f729Sjoerg 6867330f729SjoergOrder of Keys 6877330f729Sjoerg-------------- 6887330f729Sjoerg 6897330f729SjoergWhen writing out a YAML document, the keys are written in the order that the 6907330f729Sjoergcalls to mapRequired()/mapOptional() are made in the mapping() method. This 6917330f729Sjoerggives you a chance to write the fields in an order that a human reader of 6927330f729Sjoergthe YAML document would find natural. This may be different that the order 6937330f729Sjoergof the fields in the native class. 6947330f729Sjoerg 6957330f729SjoergWhen reading in a YAML document, the keys in the document can be in any order, 6967330f729Sjoergbut they are processed in the order that the calls to mapRequired()/mapOptional() 6977330f729Sjoergare made in the mapping() method. That enables some interesting 6987330f729Sjoergfunctionality. For instance, if the first field bound is the cpu and the second 6997330f729Sjoergfield bound is flags, and the flags are cpu specific, you can programmatically 7007330f729Sjoergswitch how the flags are converted to and from YAML based on the cpu. 7017330f729SjoergThis works for both reading and writing. For example: 7027330f729Sjoerg 7037330f729Sjoerg.. code-block:: c++ 7047330f729Sjoerg 7057330f729Sjoerg using llvm::yaml::MappingTraits; 7067330f729Sjoerg using llvm::yaml::IO; 7077330f729Sjoerg 7087330f729Sjoerg struct Info { 7097330f729Sjoerg CPUs cpu; 7107330f729Sjoerg uint32_t flags; 7117330f729Sjoerg }; 7127330f729Sjoerg 7137330f729Sjoerg template <> 7147330f729Sjoerg struct MappingTraits<Info> { 7157330f729Sjoerg static void mapping(IO &io, Info &info) { 7167330f729Sjoerg io.mapRequired("cpu", info.cpu); 7177330f729Sjoerg // flags must come after cpu for this to work when reading yaml 7187330f729Sjoerg if ( info.cpu == cpu_x86_64 ) 7197330f729Sjoerg io.mapRequired("flags", *(My86_64Flags*)info.flags); 7207330f729Sjoerg else 7217330f729Sjoerg io.mapRequired("flags", *(My86Flags*)info.flags); 7227330f729Sjoerg } 7237330f729Sjoerg }; 7247330f729Sjoerg 7257330f729Sjoerg 7267330f729SjoergTags 7277330f729Sjoerg---- 7287330f729Sjoerg 7297330f729SjoergThe YAML syntax supports tags as a way to specify the type of a node before 7307330f729Sjoergit is parsed. This allows dynamic types of nodes. But the YAML I/O model uses 7317330f729Sjoergstatic typing, so there are limits to how you can use tags with the YAML I/O 7327330f729Sjoergmodel. Recently, we added support to YAML I/O for checking/setting the optional 733*82d56013Sjoergtag on a map. Using this functionality it is even possible to support different 7347330f729Sjoergmappings, as long as they are convertible. 7357330f729Sjoerg 7367330f729SjoergTo check a tag, inside your mapping() method you can use io.mapTag() to specify 7377330f729Sjoergwhat the tag should be. This will also add that tag when writing yaml. 7387330f729Sjoerg 7397330f729SjoergValidation 7407330f729Sjoerg---------- 7417330f729Sjoerg 7427330f729SjoergSometimes in a yaml map, each key/value pair is valid, but the combination is 7437330f729Sjoergnot. This is similar to something having no syntax errors, but still having 7447330f729Sjoergsemantic errors. To support semantic level checking, YAML I/O allows 7457330f729Sjoergan optional ``validate()`` method in a MappingTraits template specialization. 7467330f729Sjoerg 7477330f729SjoergWhen parsing yaml, the ``validate()`` method is call *after* all key/values in 7487330f729Sjoergthe map have been processed. Any error message returned by the ``validate()`` 7497330f729Sjoergmethod during input will be printed just a like a syntax error would be printed. 7507330f729SjoergWhen writing yaml, the ``validate()`` method is called *before* the yaml 7517330f729Sjoergkey/values are written. Any error during output will trigger an ``assert()`` 7527330f729Sjoergbecause it is a programming error to have invalid struct values. 7537330f729Sjoerg 7547330f729Sjoerg 7557330f729Sjoerg.. code-block:: c++ 7567330f729Sjoerg 7577330f729Sjoerg using llvm::yaml::MappingTraits; 7587330f729Sjoerg using llvm::yaml::IO; 7597330f729Sjoerg 7607330f729Sjoerg struct Stuff { 7617330f729Sjoerg ... 7627330f729Sjoerg }; 7637330f729Sjoerg 7647330f729Sjoerg template <> 7657330f729Sjoerg struct MappingTraits<Stuff> { 7667330f729Sjoerg static void mapping(IO &io, Stuff &stuff) { 7677330f729Sjoerg ... 7687330f729Sjoerg } 7697330f729Sjoerg static StringRef validate(IO &io, Stuff &stuff) { 7707330f729Sjoerg // Look at all fields in 'stuff' and if there 7717330f729Sjoerg // are any bad values return a string describing 7727330f729Sjoerg // the error. Otherwise return an empty string. 7737330f729Sjoerg return StringRef(); 7747330f729Sjoerg } 7757330f729Sjoerg }; 7767330f729Sjoerg 7777330f729SjoergFlow Mapping 7787330f729Sjoerg------------ 7797330f729SjoergA YAML "flow mapping" is a mapping that uses the inline notation 7807330f729Sjoerg(e.g { x: 1, y: 0 } ) when written to YAML. To specify that a type should be 7817330f729Sjoergwritten in YAML using flow mapping, your MappingTraits specialization should 7827330f729Sjoergadd "static const bool flow = true;". For instance: 7837330f729Sjoerg 7847330f729Sjoerg.. code-block:: c++ 7857330f729Sjoerg 7867330f729Sjoerg using llvm::yaml::MappingTraits; 7877330f729Sjoerg using llvm::yaml::IO; 7887330f729Sjoerg 7897330f729Sjoerg struct Stuff { 7907330f729Sjoerg ... 7917330f729Sjoerg }; 7927330f729Sjoerg 7937330f729Sjoerg template <> 7947330f729Sjoerg struct MappingTraits<Stuff> { 7957330f729Sjoerg static void mapping(IO &io, Stuff &stuff) { 7967330f729Sjoerg ... 7977330f729Sjoerg } 7987330f729Sjoerg 7997330f729Sjoerg static const bool flow = true; 8007330f729Sjoerg } 8017330f729Sjoerg 8027330f729SjoergFlow mappings are subject to line wrapping according to the Output object 8037330f729Sjoergconfiguration. 8047330f729Sjoerg 8057330f729SjoergSequence 8067330f729Sjoerg======== 8077330f729Sjoerg 8087330f729SjoergTo be translated to or from a YAML sequence for your type T you must specialize 8097330f729Sjoergllvm::yaml::SequenceTraits on T and implement two methods: 8107330f729Sjoerg``size_t size(IO &io, T&)`` and 8117330f729Sjoerg``T::value_type& element(IO &io, T&, size_t indx)``. For example: 8127330f729Sjoerg 8137330f729Sjoerg.. code-block:: c++ 8147330f729Sjoerg 8157330f729Sjoerg template <> 8167330f729Sjoerg struct SequenceTraits<MySeq> { 8177330f729Sjoerg static size_t size(IO &io, MySeq &list) { ... } 8187330f729Sjoerg static MySeqEl &element(IO &io, MySeq &list, size_t index) { ... } 8197330f729Sjoerg }; 8207330f729Sjoerg 8217330f729SjoergThe size() method returns how many elements are currently in your sequence. 8227330f729SjoergThe element() method returns a reference to the i'th element in the sequence. 8237330f729SjoergWhen parsing YAML, the element() method may be called with an index one bigger 8247330f729Sjoergthan the current size. Your element() method should allocate space for one 8257330f729Sjoergmore element (using default constructor if element is a C++ object) and returns 8267330f729Sjoerga reference to that new allocated space. 8277330f729Sjoerg 8287330f729Sjoerg 8297330f729SjoergFlow Sequence 8307330f729Sjoerg------------- 8317330f729SjoergA YAML "flow sequence" is a sequence that when written to YAML it uses the 8327330f729Sjoerginline notation (e.g [ foo, bar ] ). To specify that a sequence type should 8337330f729Sjoergbe written in YAML as a flow sequence, your SequenceTraits specialization should 8347330f729Sjoergadd "static const bool flow = true;". For instance: 8357330f729Sjoerg 8367330f729Sjoerg.. code-block:: c++ 8377330f729Sjoerg 8387330f729Sjoerg template <> 8397330f729Sjoerg struct SequenceTraits<MyList> { 8407330f729Sjoerg static size_t size(IO &io, MyList &list) { ... } 8417330f729Sjoerg static MyListEl &element(IO &io, MyList &list, size_t index) { ... } 8427330f729Sjoerg 8437330f729Sjoerg // The existence of this member causes YAML I/O to use a flow sequence 8447330f729Sjoerg static const bool flow = true; 8457330f729Sjoerg }; 8467330f729Sjoerg 8477330f729SjoergWith the above, if you used MyList as the data type in your native data 8487330f729Sjoergstructures, then when converted to YAML, a flow sequence of integers 8497330f729Sjoergwill be used (e.g. [ 10, -3, 4 ]). 8507330f729Sjoerg 8517330f729SjoergFlow sequences are subject to line wrapping according to the Output object 8527330f729Sjoergconfiguration. 8537330f729Sjoerg 8547330f729SjoergUtility Macros 8557330f729Sjoerg-------------- 8567330f729SjoergSince a common source of sequences is std::vector<>, YAML I/O provides macros: 8577330f729SjoergLLVM_YAML_IS_SEQUENCE_VECTOR() and LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR() which 8587330f729Sjoergcan be used to easily specify SequenceTraits<> on a std::vector type. YAML 8597330f729SjoergI/O does not partial specialize SequenceTraits on std::vector<> because that 8607330f729Sjoergwould force all vectors to be sequences. An example use of the macros: 8617330f729Sjoerg 8627330f729Sjoerg.. code-block:: c++ 8637330f729Sjoerg 8647330f729Sjoerg std::vector<MyType1>; 8657330f729Sjoerg std::vector<MyType2>; 8667330f729Sjoerg LLVM_YAML_IS_SEQUENCE_VECTOR(MyType1) 8677330f729Sjoerg LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(MyType2) 8687330f729Sjoerg 8697330f729Sjoerg 8707330f729Sjoerg 8717330f729SjoergDocument List 8727330f729Sjoerg============= 8737330f729Sjoerg 8747330f729SjoergYAML allows you to define multiple "documents" in a single YAML file. Each 8757330f729Sjoergnew document starts with a left aligned "---" token. The end of all documents 8767330f729Sjoergis denoted with a left aligned "..." token. Many users of YAML will never 8777330f729Sjoerghave need for multiple documents. The top level node in their YAML schema 8787330f729Sjoergwill be a mapping or sequence. For those cases, the following is not needed. 8797330f729SjoergBut for cases where you do want multiple documents, you can specify a 8807330f729Sjoergtrait for you document list type. The trait has the same methods as 8817330f729SjoergSequenceTraits but is named DocumentListTraits. For example: 8827330f729Sjoerg 8837330f729Sjoerg.. code-block:: c++ 8847330f729Sjoerg 8857330f729Sjoerg template <> 8867330f729Sjoerg struct DocumentListTraits<MyDocList> { 8877330f729Sjoerg static size_t size(IO &io, MyDocList &list) { ... } 8887330f729Sjoerg static MyDocType element(IO &io, MyDocList &list, size_t index) { ... } 8897330f729Sjoerg }; 8907330f729Sjoerg 8917330f729Sjoerg 8927330f729SjoergUser Context Data 8937330f729Sjoerg================= 8947330f729SjoergWhen an llvm::yaml::Input or llvm::yaml::Output object is created their 8957330f729Sjoergconstructors take an optional "context" parameter. This is a pointer to 8967330f729Sjoergwhatever state information you might need. 8977330f729Sjoerg 8987330f729SjoergFor instance, in a previous example we showed how the conversion type for a 8997330f729Sjoergflags field could be determined at runtime based on the value of another field 9007330f729Sjoergin the mapping. But what if an inner mapping needs to know some field value 9017330f729Sjoergof an outer mapping? That is where the "context" parameter comes in. You 9027330f729Sjoergcan set values in the context in the outer map's mapping() method and 9037330f729Sjoergretrieve those values in the inner map's mapping() method. 9047330f729Sjoerg 9057330f729SjoergThe context value is just a void*. All your traits which use the context 9067330f729Sjoergand operate on your native data types, need to agree what the context value 9077330f729Sjoergactually is. It could be a pointer to an object or struct which your various 9087330f729Sjoergtraits use to shared context sensitive information. 9097330f729Sjoerg 9107330f729Sjoerg 9117330f729SjoergOutput 9127330f729Sjoerg====== 9137330f729Sjoerg 9147330f729SjoergThe llvm::yaml::Output class is used to generate a YAML document from your 9157330f729Sjoergin-memory data structures, using traits defined on your data types. 9167330f729SjoergTo instantiate an Output object you need an llvm::raw_ostream, an optional 9177330f729Sjoergcontext pointer and an optional wrapping column: 9187330f729Sjoerg 9197330f729Sjoerg.. code-block:: c++ 9207330f729Sjoerg 9217330f729Sjoerg class Output : public IO { 9227330f729Sjoerg public: 9237330f729Sjoerg Output(llvm::raw_ostream &, void *context = NULL, int WrapColumn = 70); 9247330f729Sjoerg 9257330f729SjoergOnce you have an Output object, you can use the C++ stream operator on it 9267330f729Sjoergto write your native data as YAML. One thing to recall is that a YAML file 9277330f729Sjoergcan contain multiple "documents". If the top level data structure you are 9287330f729Sjoergstreaming as YAML is a mapping, scalar, or sequence, then Output assumes you 9297330f729Sjoergare generating one document and wraps the mapping output 9307330f729Sjoergwith "``---``" and trailing "``...``". 9317330f729Sjoerg 9327330f729SjoergThe WrapColumn parameter will cause the flow mappings and sequences to 9337330f729Sjoergline-wrap when they go over the supplied column. Pass 0 to completely 9347330f729Sjoergsuppress the wrapping. 9357330f729Sjoerg 9367330f729Sjoerg.. code-block:: c++ 9377330f729Sjoerg 9387330f729Sjoerg using llvm::yaml::Output; 9397330f729Sjoerg 9407330f729Sjoerg void dumpMyMapDoc(const MyMapType &info) { 9417330f729Sjoerg Output yout(llvm::outs()); 9427330f729Sjoerg yout << info; 9437330f729Sjoerg } 9447330f729Sjoerg 9457330f729SjoergThe above could produce output like: 9467330f729Sjoerg 9477330f729Sjoerg.. code-block:: yaml 9487330f729Sjoerg 9497330f729Sjoerg --- 9507330f729Sjoerg name: Tom 9517330f729Sjoerg hat-size: 7 9527330f729Sjoerg ... 9537330f729Sjoerg 9547330f729SjoergOn the other hand, if the top level data structure you are streaming as YAML 9557330f729Sjoerghas a DocumentListTraits specialization, then Output walks through each element 9567330f729Sjoergof your DocumentList and generates a "---" before the start of each element 9577330f729Sjoergand ends with a "...". 9587330f729Sjoerg 9597330f729Sjoerg.. code-block:: c++ 9607330f729Sjoerg 9617330f729Sjoerg using llvm::yaml::Output; 9627330f729Sjoerg 9637330f729Sjoerg void dumpMyMapDoc(const MyDocListType &docList) { 9647330f729Sjoerg Output yout(llvm::outs()); 9657330f729Sjoerg yout << docList; 9667330f729Sjoerg } 9677330f729Sjoerg 9687330f729SjoergThe above could produce output like: 9697330f729Sjoerg 9707330f729Sjoerg.. code-block:: yaml 9717330f729Sjoerg 9727330f729Sjoerg --- 9737330f729Sjoerg name: Tom 9747330f729Sjoerg hat-size: 7 9757330f729Sjoerg --- 9767330f729Sjoerg name: Tom 9777330f729Sjoerg shoe-size: 11 9787330f729Sjoerg ... 9797330f729Sjoerg 9807330f729SjoergInput 9817330f729Sjoerg===== 9827330f729Sjoerg 9837330f729SjoergThe llvm::yaml::Input class is used to parse YAML document(s) into your native 9847330f729Sjoergdata structures. To instantiate an Input 9857330f729Sjoergobject you need a StringRef to the entire YAML file, and optionally a context 9867330f729Sjoergpointer: 9877330f729Sjoerg 9887330f729Sjoerg.. code-block:: c++ 9897330f729Sjoerg 9907330f729Sjoerg class Input : public IO { 9917330f729Sjoerg public: 9927330f729Sjoerg Input(StringRef inputContent, void *context=NULL); 9937330f729Sjoerg 9947330f729SjoergOnce you have an Input object, you can use the C++ stream operator to read 9957330f729Sjoergthe document(s). If you expect there might be multiple YAML documents in 9967330f729Sjoergone file, you'll need to specialize DocumentListTraits on a list of your 9977330f729Sjoergdocument type and stream in that document list type. Otherwise you can 9987330f729Sjoergjust stream in the document type. Also, you can check if there was 9997330f729Sjoergany syntax errors in the YAML be calling the error() method on the Input 10007330f729Sjoergobject. For example: 10017330f729Sjoerg 10027330f729Sjoerg.. code-block:: c++ 10037330f729Sjoerg 10047330f729Sjoerg // Reading a single document 10057330f729Sjoerg using llvm::yaml::Input; 10067330f729Sjoerg 10077330f729Sjoerg Input yin(mb.getBuffer()); 10087330f729Sjoerg 10097330f729Sjoerg // Parse the YAML file 10107330f729Sjoerg MyDocType theDoc; 10117330f729Sjoerg yin >> theDoc; 10127330f729Sjoerg 10137330f729Sjoerg // Check for error 10147330f729Sjoerg if ( yin.error() ) 10157330f729Sjoerg return; 10167330f729Sjoerg 10177330f729Sjoerg 10187330f729Sjoerg.. code-block:: c++ 10197330f729Sjoerg 10207330f729Sjoerg // Reading multiple documents in one file 10217330f729Sjoerg using llvm::yaml::Input; 10227330f729Sjoerg 10237330f729Sjoerg LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MyDocType) 10247330f729Sjoerg 10257330f729Sjoerg Input yin(mb.getBuffer()); 10267330f729Sjoerg 10277330f729Sjoerg // Parse the YAML file 10287330f729Sjoerg std::vector<MyDocType> theDocList; 10297330f729Sjoerg yin >> theDocList; 10307330f729Sjoerg 10317330f729Sjoerg // Check for error 10327330f729Sjoerg if ( yin.error() ) 10337330f729Sjoerg return; 10347330f729Sjoerg 10357330f729Sjoerg 1036