Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/core/yaml/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME yaml
PRIVATE_HEADERS error.h
SOURCES yaml.cc lexer.h parser.h stringify.h)
PRIVATE_HEADERS error.h roundtrip.h
SOURCES yaml.cc roundtrip.cc lexer.h parser.h stringify.h)

if(SOURCEMETA_CORE_INSTALL)
sourcemeta_library_install(NAMESPACE sourcemeta PROJECT core NAME yaml)
endif()

target_link_libraries(sourcemeta_core_yaml PUBLIC sourcemeta::core::json)
target_link_libraries(sourcemeta_core_yaml PUBLIC sourcemeta::core::jsonpointer)
target_link_libraries(sourcemeta_core_yaml PRIVATE sourcemeta::core::io)
target_link_libraries(sourcemeta_core_yaml PRIVATE sourcemeta::core::unicode)
41 changes: 41 additions & 0 deletions src/core/yaml/include/sourcemeta/core/yaml.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

// NOLINTBEGIN(misc-include-cleaner)
#include <sourcemeta/core/yaml_error.h>
#include <sourcemeta/core/yaml_roundtrip.h>
// NOLINTEND(misc-include-cleaner)

#include <filesystem> // std::filesystem
Expand Down Expand Up @@ -110,6 +111,46 @@ SOURCEMETA_CORE_YAML_EXPORT
auto read_yaml_or_json(const std::filesystem::path &path,
const JSON::ParseCallback &callback = nullptr) -> JSON;

/// @ingroup yaml
///
/// Create a JSON document from a YAML string, collecting round-trip metadata
/// to reproduce the original formatting. For example:
///
/// ```cpp
/// #include <sourcemeta/core/json.h>
/// #include <sourcemeta/core/yaml.h>
///
/// sourcemeta::core::YAMLRoundTrip roundtrip;
/// const std::string input{"hello: world"};
/// const sourcemeta::core::JSON document =
/// sourcemeta::core::parse_yaml(input, roundtrip);
/// ```
SOURCEMETA_CORE_YAML_EXPORT
auto parse_yaml(const JSON::String &input, YAMLRoundTrip &roundtrip,
const JSON::ParseCallback &callback = nullptr) -> JSON;

/// @ingroup yaml
///
/// Stringify a JSON document as YAML, using round-trip metadata collected
/// during parsing to preserve the original formatting. For example:
///
/// ```cpp
/// #include <sourcemeta/core/json.h>
/// #include <sourcemeta/core/yaml.h>
///
/// #include <iostream>
///
/// sourcemeta::core::YAMLRoundTrip roundtrip;
/// const std::string input{"hello: world"};
/// const sourcemeta::core::JSON document =
/// sourcemeta::core::parse_yaml(input, roundtrip);
/// sourcemeta::core::stringify_yaml(document, std::cout, roundtrip);
/// ```
SOURCEMETA_CORE_YAML_EXPORT
auto stringify_yaml(const JSON &document,
std::basic_ostream<JSON::Char, JSON::CharTraits> &stream,
const YAMLRoundTrip &roundtrip) -> void;

/// @ingroup yaml
///
/// Stringify a JSON document as YAML. For example:
Expand Down
68 changes: 68 additions & 0 deletions src/core/yaml/include/sourcemeta/core/yaml_roundtrip.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#ifndef SOURCEMETA_CORE_YAML_ROUNDTRIP_H_
#define SOURCEMETA_CORE_YAML_ROUNDTRIP_H_

#ifndef SOURCEMETA_CORE_YAML_EXPORT
#include <sourcemeta/core/yaml_export.h>
#endif

#include <sourcemeta/core/json.h>
#include <sourcemeta/core/jsonpointer.h>

#include <cstdint> // std::uint8_t, std::size_t
#include <map> // std::map
#include <optional> // std::optional
#include <string> // std::string
#include <vector> // std::vector

namespace sourcemeta::core {

/// @ingroup yaml
enum class YAMLScalarStyle : std::uint8_t {
Plain,
SingleQuoted,
DoubleQuoted,
Literal,
Folded
};

/// @ingroup yaml
enum class YAMLCollectionStyle : std::uint8_t { Block, Flow };

/// @ingroup yaml
enum class YAMLChomping : std::uint8_t { Clip, Strip, Keep };

/// @ingroup yaml
struct SOURCEMETA_CORE_YAML_EXPORT YAMLNodeStyle {
std::optional<YAMLScalarStyle> scalar;
std::optional<YAMLCollectionStyle> collection;
std::optional<YAMLChomping> chomping;
std::optional<std::string> block_content;
std::optional<std::string> anchor;
std::vector<std::string> comments_before;
std::optional<std::string> comment_inline;
std::optional<std::string> comment_on_indicator;
};

/// @ingroup yaml
/// Holds per-node metadata collected during YAML parsing to reproduce the
/// original formatting
class SOURCEMETA_CORE_YAML_EXPORT YAMLRoundTrip {
public:
auto clear() -> void;
std::map<Pointer, YAMLNodeStyle> styles;
std::map<Pointer, std::string> aliases;
std::map<Pointer, YAMLScalarStyle> key_styles;
bool explicit_document_start{false};
bool explicit_document_end{false};
std::optional<std::string> document_start_comment;
std::optional<std::string> document_end_comment;
std::vector<std::string> leading_comments;
std::vector<std::string> post_start_comments;
std::vector<std::string> pre_end_comments;
std::vector<std::string> trailing_comments;
std::size_t indent_width{2};
};

} // namespace sourcemeta::core

#endif
Loading
Loading