Skip to content

Commit

Permalink
[DEBUG] Run YAML parser without walking document
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
  • Loading branch information
jviotti committed Jan 14, 2025
1 parent b931c8d commit d067993
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 115 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ jobs:
# Not every CTest version supports the --test-dir option. If such option
# is not recognized, `ctest` will successfully exit finding no tests.
# Better to be sure and `cd` all the time here.
- run: cd ./build && ctest --build-config Release --output-on-failure --parallel
- run: cd ./build && ctest --build-config Release --output-on-failure --parallel --verbose
env:
# See https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
UBSAN_OPTIONS: print_stacktrace=1
Expand Down
49 changes: 5 additions & 44 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(vendor/noa/cmake/noa.cmake)

# Options
option(JSONTOOLKIT_URI "Build the JSON Toolkit URI library" ON)
option(JSONTOOLKIT_URI "Build the JSON Toolkit URI library" OFF)
option(JSONTOOLKIT_JSON "Build the JSON Toolkit JSON library" ON)
option(JSONTOOLKIT_REGEX "Build the JSON Toolkit Regex library" ON)
option(JSONTOOLKIT_JSONSCHEMA "Build the JSON Toolkit JSON Schema library" ON)
option(JSONTOOLKIT_JSONPOINTER "Build the JSON Toolkit JSON Pointer library" ON)
option(JSONTOOLKIT_JSONL "Build the JSON Toolkit JSONL library" ON)
option(JSONTOOLKIT_REGEX "Build the JSON Toolkit Regex library" OFF)
option(JSONTOOLKIT_JSONSCHEMA "Build the JSON Toolkit JSON Schema library" OFF)
option(JSONTOOLKIT_JSONPOINTER "Build the JSON Toolkit JSON Pointer library" OFF)
option(JSONTOOLKIT_JSONL "Build the JSON Toolkit JSONL library" OFF)
option(JSONTOOLKIT_YAML "Build the JSON Toolkit YAML library" ON)
option(JSONTOOLKIT_TESTS "Build the JSON Toolkit tests" OFF)
option(JSONTOOLKIT_BENCHMARK "Build the JSON Toolkit benchmarks" OFF)
Expand Down Expand Up @@ -94,46 +94,7 @@ endif()
if(JSONTOOLKIT_TESTS)
find_package(GoogleTest REQUIRED)
enable_testing()

if(JSONTOOLKIT_URI)
add_subdirectory(test/uri)
endif()

if(JSONTOOLKIT_JSON)
add_subdirectory(test/json)
endif()

if(JSONTOOLKIT_JSON AND JSONTOOLKIT_REGEX)
add_subdirectory(test/regex)
endif()

if(JSONTOOLKIT_JSON AND JSONTOOLKIT_JSONPOINTER)
add_subdirectory(test/jsonpointer)
endif()

if(JSONTOOLKIT_URI AND JSONTOOLKIT_JSON AND
JSONTOOLKIT_JSONPOINTER AND JSONTOOLKIT_JSONSCHEMA)
add_subdirectory(test/jsonschema)
endif()

if(JSONTOOLKIT_JSON AND JSONTOOLKIT_JSONL)
add_subdirectory(test/jsonl)
endif()

if(JSONTOOLKIT_JSON AND JSONTOOLKIT_YAML)
add_subdirectory(test/yaml)
endif()

if(PROJECT_IS_TOP_LEVEL)
# Otherwise we need the child project to link
# against the sanitizers too.
if(NOT JSONTOOLKIT_ADDRESS_SANITIZER AND NOT JSONTOOLKIT_UNDEFINED_SANITIZER)
add_subdirectory(test/packaging)
endif()
endif()

if(JSONTOOLKIT_BENCHMARK)
find_package(GoogleBenchmark REQUIRED)
add_subdirectory(benchmark)
endif()
endif()
62 changes: 3 additions & 59 deletions src/yaml/yaml.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,63 +5,6 @@
// See https://pyyaml.org/wiki/LibYAML for basic documentation
#include <yaml.h>

static auto yaml_node_to_json(yaml_node_t *const node,
yaml_document_t *const document)
-> sourcemeta::jsontoolkit::JSON {
if (!node) {
return sourcemeta::jsontoolkit::JSON{nullptr};
}

switch (node->type) {
case YAML_SCALAR_NODE: {
const std::string_view input{
reinterpret_cast<char *>(node->data.scalar.value),
node->data.scalar.length};

try {
// TODO: Avoid this std::string transformation
return sourcemeta::jsontoolkit::parse(std::string{input});
// Looks like it is very hard in YAML, given a scalar value, to
// determine whether it is a string or something else without attempting
// to parsing it and potentially failing to do so
} catch (const sourcemeta::jsontoolkit::ParseError &) {
return sourcemeta::jsontoolkit::JSON{input};
}
}

case YAML_SEQUENCE_NODE: {
auto result{sourcemeta::jsontoolkit::JSON::make_array()};
for (yaml_node_item_t *item = node->data.sequence.items.start;
item < node->data.sequence.items.top; ++item) {
yaml_node_t *const child = yaml_document_get_node(document, *item);
result.push_back(yaml_node_to_json(child, document));
}

return result;
}

case YAML_MAPPING_NODE: {
auto result{sourcemeta::jsontoolkit::JSON::make_object()};
for (yaml_node_pair_t *pair = node->data.mapping.pairs.start;
pair < node->data.mapping.pairs.top; ++pair) {
yaml_node_t *const key_node =
yaml_document_get_node(document, pair->key);
yaml_node_t *const value_node =
yaml_document_get_node(document, pair->value);
if (key_node && key_node->type == YAML_SCALAR_NODE) {
result.assign(reinterpret_cast<char *>(key_node->data.scalar.value),
yaml_node_to_json(value_node, document));
}
}

return result;
}

default:
return sourcemeta::jsontoolkit::JSON{nullptr};
}
}

namespace sourcemeta::jsontoolkit {

auto from_yaml(const JSON::String &input) -> JSON {
Expand All @@ -72,7 +15,8 @@ auto from_yaml(const JSON::String &input) -> JSON {

yaml_parser_set_input_string(
&parser, reinterpret_cast<const unsigned char *>(input.c_str()),
input.size());
// The size plus the null terminator, which libyaml expects to see
input.size() + 1);

yaml_document_t document;
if (!yaml_parser_load(&parser, &document)) {
Expand All @@ -89,7 +33,7 @@ auto from_yaml(const JSON::String &input) -> JSON {
}

try {
const auto result{yaml_node_to_json(root, &document)};
const sourcemeta::jsontoolkit::JSON result{nullptr};
yaml_document_delete(&document);
yaml_parser_delete(&parser);
return result;
Expand Down
2 changes: 1 addition & 1 deletion test/yaml/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ target_link_libraries(sourcemeta_jsontoolkit_yaml_unit
PRIVATE sourcemeta::jsontoolkit::yaml)
set_target_properties(sourcemeta_jsontoolkit_yaml_unit
PROPERTIES FOLDER "JSON Toolkit/YAML")
add_test(NAME YAML COMMAND sourcemeta_jsontoolkit_yaml_unit --gtest_brief=1)
add_test(NAME YAML COMMAND sourcemeta_jsontoolkit_yaml_unit)
15 changes: 5 additions & 10 deletions test/yaml/yaml_parse_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,9 @@
#include <sourcemeta/jsontoolkit/json.h>
#include <sourcemeta/jsontoolkit/yaml.h>

TEST(YAML_parse, empty) {
const std::string input{""};
EXPECT_THROW(sourcemeta::jsontoolkit::from_yaml(input),
sourcemeta::jsontoolkit::YAMLParseError);
}

TEST(YAML_parse, blank) {
const std::string input{" "};
EXPECT_THROW(sourcemeta::jsontoolkit::from_yaml(input),
sourcemeta::jsontoolkit::YAMLParseError);
TEST(YAML_parse, scalar_1) {
const std::string input{"1"};
const auto result{sourcemeta::jsontoolkit::from_yaml(input)};
const sourcemeta::jsontoolkit::JSON expected{nullptr};
EXPECT_EQ(result, expected);
}
12 changes: 12 additions & 0 deletions vendor/yaml/src/parser.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d067993

Please sign in to comment.