event-based parser policy pt3: samples and tools

This commit is contained in:
Joao Paulo Magalhaes
2024-05-05 18:52:11 +02:00
parent ad7568f4d2
commit 0881aa241c
5 changed files with 501 additions and 215 deletions

1
.gitignore vendored
View File

@@ -33,6 +33,7 @@ build/
install/ install/
.python-version .python-version
compile_commands.json compile_commands.json
launch.json
# test files # test files
/Testing/ /Testing/

View File

@@ -67,6 +67,7 @@ void sample_emit_style(); ///< set the nodes to FLOW/BLOCK format
void sample_json(); ///< JSON parsing and emitting void sample_json(); ///< JSON parsing and emitting
void sample_anchors_and_aliases(); ///< deal with YAML anchors and aliases void sample_anchors_and_aliases(); ///< deal with YAML anchors and aliases
void sample_tags(); ///< deal with YAML type tags void sample_tags(); ///< deal with YAML type tags
void sample_tag_directives(); ///< deal with YAML tag namespace directives
void sample_docs(); ///< deal with YAML docs void sample_docs(); ///< deal with YAML docs
void sample_error_handler(); ///< set a custom error handler void sample_error_handler(); ///< set a custom error handler
void sample_global_allocator(); ///< set a global allocator for ryml void sample_global_allocator(); ///< set a global allocator for ryml
@@ -106,6 +107,7 @@ int main()
sample::sample_json(); sample::sample_json();
sample::sample_anchors_and_aliases(); sample::sample_anchors_and_aliases();
sample::sample_tags(); sample::sample_tags();
sample::sample_tag_directives();
sample::sample_docs(); sample::sample_docs();
sample::sample_error_handler(); sample::sample_error_handler();
sample::sample_global_allocator(); sample::sample_global_allocator();
@@ -209,12 +211,13 @@ struct CheckPredicate
{ {
const char *file; const char *file;
const int line; const int line;
void operator() (bool predicate) const void operator() (bool predicate) const
{ {
if (!report_check(line, nullptr, predicate)) if (!report_check(line, nullptr, predicate))
{ {
#ifdef RYML_DBG
RYML_DEBUG_BREAK(); RYML_DEBUG_BREAK();
#endif
} }
} }
}; };
@@ -296,22 +299,16 @@ void sample_lightning_overview()
// emit tree // emit tree
// to std::string // to std::string
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(foo: 1 CHECK(ryml::emitrs_yaml<std::string>(tree) == R"({foo: 1,bar: [10,11,12],john: doe})");
bar: std::cout << tree; // emit to ostream
- 10 ryml::emit_yaml(tree, stdout); // emit to FILE*
- 11
- 12
john: doe
)");
std::cout << tree; // emit to stdout
ryml::emit_yaml(tree, stdout); // emit to file
// emit node // emit node
ryml::ConstNodeRef foo = tree["foo"]; ryml::ConstNodeRef foo = tree["foo"];
// to std::string // to std::string
CHECK(ryml::emitrs_yaml<std::string>(foo) == "foo: 1\n"); CHECK(ryml::emitrs_yaml<std::string>(foo) == "foo: 1\n");
std::cout << foo; // emit node to stdout std::cout << foo; // emit node to ostream
ryml::emit_yaml(foo, stdout); // emit node to file ryml::emit_yaml(foo, stdout); // emit node to FILE*
} }
@@ -321,7 +318,10 @@ john: doe
void sample_quick_overview() void sample_quick_overview()
{ {
// Parse YAML code in place, potentially mutating the buffer: // Parse YAML code in place, potentially mutating the buffer:
char yml_buf[] = "{foo: 1, bar: [2, 3], john: doe}"; char yml_buf[] = R"(
foo: 1
bar: [2, 3]
john: doe)";
ryml::Tree tree = ryml::parse_in_place(yml_buf); ryml::Tree tree = ryml::parse_in_place(yml_buf);
// The resulting tree contains only views to the parsed string. If // The resulting tree contains only views to the parsed string. If
@@ -797,11 +797,7 @@ void sample_quick_overview()
// Emitting: // Emitting:
ryml::csubstr expected_result = R"(foo: says who ryml::csubstr expected_result = R"(foo: says who
bar: bar: [20,30,oh so nice,oh so nice (serialized)]
- 20
- 30
- oh so nice
- oh so nice (serialized)
john: in_scope john: in_scope
float: 2.4 float: 2.4
digits: 2.400000 digits: 2.400000
@@ -871,21 +867,20 @@ I am something: indeed
// Getting the location of nodes in the source: // Getting the location of nodes in the source:
// //
// Location tracking is opt-in: // Location tracking is opt-in:
ryml::Parser parser(ryml::ParserOptions().locations(true)); ryml::EventHandlerTree evt_handler = {};
ryml::Parser parser(&evt_handler, ryml::ParserOptions().locations(true));
// Now the parser will start by building the accelerator structure: // Now the parser will start by building the accelerator structure:
ryml::Tree tree2; ryml::Tree tree2 = parse_in_arena(&parser, "expected.yml", expected_result);
parser.parse_in_arena("expected.yml", expected_result, &tree2);
// ... and use it when querying // ... and use it when querying
ryml::ConstNodeRef subject_node = tree2["bar"][1]; ryml::ConstNodeRef subject_node = tree2["bar"][1];
CHECK(subject_node.val() == "30"); CHECK(subject_node.val() == "30");
ryml::Location loc = parser.location(subject_node); ryml::Location loc = parser.location(subject_node);
CHECK(parser.location_contents(loc).begins_with("30")); CHECK(parser.location_contents(loc).begins_with("30"));
CHECK(loc.line == 3u); CHECK(loc.line == 1u);
CHECK(loc.col == 4u); CHECK(loc.col == 9u);
// For further details in location tracking, // For further details in location tracking,
// refer to the sample function below. // refer to the sample function below.
//------------------------------------------------------------------ //------------------------------------------------------------------
// Dealing with UTF8 // Dealing with UTF8
ryml::Tree langs = ryml::parse_in_arena(R"( ryml::Tree langs = ryml::parse_in_arena(R"(
@@ -975,7 +970,7 @@ void sample_substr()
// //
// Since the input is only a pointer, the string length can only // Since the input is only a pointer, the string length can only
// be found with a call to strlen(). To make this cost evident, we // be found with a call to strlen(). To make this cost evident, we
// require a call to to_csubstr(): // require calling to_csubstr():
{ {
const char *foobar_str = "foobar"; const char *foobar_str = "foobar";
ryml::csubstr s = ryml::to_csubstr(foobar_str); ryml::csubstr s = ryml::to_csubstr(foobar_str);
@@ -989,8 +984,10 @@ void sample_substr()
// construct from a std::string: same approach as above. // construct from a std::string: same approach as above.
// requires inclusion of the <ryml/std/string.hpp> header // requires inclusion of the <ryml/std/string.hpp> header
// or of the umbrella header <ryml_std.hpp>. // or of the umbrella header <ryml_std.hpp>.
// this was a deliberate design choice to avoid requiring //
// the heavy std:: allocation machinery // not including <string> in the default header is a deliberate
// design choice to avoid including the heavy std:: allocation
// machinery
{ {
std::string foobar_str = "foobar"; std::string foobar_str = "foobar";
ryml::csubstr s = ryml::to_csubstr(foobar_str); // defined in <ryml/std/string.hpp> ryml::csubstr s = ryml::to_csubstr(foobar_str); // defined in <ryml/std/string.hpp>
@@ -1787,7 +1784,7 @@ void sample_parse_in_place()
char src[] = "{foo: 1, bar: [2, 3]}"; // ryml can parse in situ char src[] = "{foo: 1, bar: [2, 3]}"; // ryml can parse in situ
ryml::substr srcview = src; // a mutable view to the source buffer ryml::substr srcview = src; // a mutable view to the source buffer
ryml::Tree tree = ryml::parse_in_place(srcview); // you can also reuse the tree and/or parser ryml::Tree tree = ryml::parse_in_place(srcview); // you can also reuse the tree and/or parser
ryml::ConstNodeRef root = tree.crootref(); // get a reference to the root ryml::ConstNodeRef root = tree.crootref(); // get a constant reference to the root
CHECK(root.is_map()); CHECK(root.is_map());
CHECK(root["foo"].is_keyval()); CHECK(root["foo"].is_keyval());
@@ -1839,7 +1836,7 @@ void sample_parse_in_arena()
// to parse read-only memory, ryml will copy first to the tree's // to parse read-only memory, ryml will copy first to the tree's
// arena, and then parse the copied buffer: // arena, and then parse the copied buffer:
ryml::Tree tree = ryml::parse_in_arena("{foo: 1, bar: [2, 3]}"); ryml::Tree tree = ryml::parse_in_arena("{foo: 1, bar: [2, 3]}");
ryml::ConstNodeRef root = tree.crootref(); // get a reference to the root ryml::ConstNodeRef root = tree.crootref(); // get a const reference to the root
CHECK(root.is_map()); CHECK(root.is_map());
CHECK(root["foo"].is_keyval()); CHECK(root["foo"].is_keyval());
@@ -1891,7 +1888,10 @@ void sample_parse_reuse_tree()
tree.reserve_arena(256); // reserve 256 characters (good enough for this sample) tree.reserve_arena(256); // reserve 256 characters (good enough for this sample)
// now parse into the tree: // now parse into the tree:
ryml::parse_in_arena("{foo: 1, bar: [2, 3]}", &tree); ryml::csubstr yaml = R"(foo: 1
bar: [2, 3]
)";
ryml::parse_in_arena(yaml, &tree);
ryml::ConstNodeRef root = tree.crootref(); ryml::ConstNodeRef root = tree.crootref();
CHECK(root.num_children() == 2); CHECK(root.num_children() == 2);
@@ -1905,21 +1905,15 @@ void sample_parse_reuse_tree()
CHECK(root["bar"][0].val() == "2"); CHECK(root["bar"][0].val() == "2");
CHECK(root["bar"][1].val() == "3"); CHECK(root["bar"][1].val() == "3");
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(foo: 1 CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(foo: 1
bar: bar: [2,3]
- 2
- 3
)"); )");
// WATCHOUT: parsing into an existing tree will APPEND to it: // WATCHOUT: parsing into an existing tree will APPEND to it:
ryml::parse_in_arena("{foo2: 12, bar2: [22, 32]}", &tree); ryml::parse_in_arena("{foo2: 12, bar2: [22, 32]}", &tree);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(foo: 1 CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(foo: 1
bar: bar: [2,3]
- 2
- 3
foo2: 12 foo2: 12
bar2: bar2: [22,32]
- 22
- 32
)"); )");
CHECK(root.num_children() == 4); CHECK(root.num_children() == 4);
CHECK(root["foo2"].is_keyval()); CHECK(root["foo2"].is_keyval());
@@ -1934,12 +1928,8 @@ bar2:
// clear first before parsing into an existing tree. // clear first before parsing into an existing tree.
tree.clear(); tree.clear();
tree.clear_arena(); // you may or may not want to clear the arena tree.clear_arena(); // you may or may not want to clear the arena
ryml::parse_in_arena("[a, b, {x0: 1, x1: 2}]", &tree); ryml::parse_in_arena("- a\n- b\n- {x0: 1, x1: 2}", &tree);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a CHECK(ryml::emitrs_yaml<std::string>(tree) == "- a\n- b\n- {x0: 1,x1: 2}\n");
- b
- x0: 1
x1: 2
)");
CHECK(root.is_seq()); CHECK(root.is_seq());
CHECK(root[0].val() == "a"); CHECK(root[0].val() == "a");
CHECK(root[1].val() == "b"); CHECK(root[1].val() == "b");
@@ -1949,11 +1939,10 @@ bar2:
// we can parse directly into a node nested deep in an existing tree: // we can parse directly into a node nested deep in an existing tree:
ryml::NodeRef mroot = tree.rootref(); // modifiable root ryml::NodeRef mroot = tree.rootref(); // modifiable root
ryml::parse_in_arena("{champagne: Dom Perignon, coffee: Arabica}", mroot.append_child()); ryml::parse_in_arena("champagne: Dom Perignon\ncoffee: Arabica", mroot.append_child());
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a
- b - b
- x0: 1 - {x0: 1,x1: 2}
x1: 2
- champagne: Dom Perignon - champagne: Dom Perignon
coffee: Arabica coffee: Arabica
)"); )");
@@ -1967,15 +1956,17 @@ bar2:
CHECK(root[3]["champagne"].val() == "Dom Perignon"); CHECK(root[3]["champagne"].val() == "Dom Perignon");
CHECK(root[3]["coffee"].val() == "Arabica"); CHECK(root[3]["coffee"].val() == "Arabica");
// watchout: to add to an existing node within a map, the node's key must first be set: // watchout: to add to an existing node within a map, the node's
// key must be separately set first:
ryml::NodeRef more = mroot[3].append_child({ryml::KEYMAP, "more"}); ryml::NodeRef more = mroot[3].append_child({ryml::KEYMAP, "more"});
ryml::NodeRef beer = mroot[3].append_child({ryml::KEYSEQ, "beer"}); ryml::NodeRef beer = mroot[3].append_child({ryml::KEYSEQ, "beer"});
ryml::NodeRef always = mroot[3].append_child({ryml::KEY, "always"});
ryml::parse_in_arena("{vinho verde: Soalheiro, vinho tinto: Redoma 2017}", more); ryml::parse_in_arena("{vinho verde: Soalheiro, vinho tinto: Redoma 2017}", more);
ryml::parse_in_arena("[Rochefort 10, Busch, Leffe Rituel]", beer); ryml::parse_in_arena("- Rochefort 10\n- Busch\n- Leffe Rituel", beer);
ryml::parse_in_arena("lots\nof\nwater", always);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a
- b - b
- x0: 1 - {x0: 1,x1: 2}
x1: 2
- champagne: Dom Perignon - champagne: Dom Perignon
coffee: Arabica coffee: Arabica
more: more:
@@ -1985,13 +1976,14 @@ bar2:
- Rochefort 10 - Rochefort 10
- Busch - Busch
- Leffe Rituel - Leffe Rituel
always: lots of water
)"); )");
ryml::parse_in_arena("[foo, bar, baz, bat]", mroot); // can append at the top:
ryml::parse_in_arena("- foo\n- bar\n- baz\n- bat", mroot);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a
- b - b
- x0: 1 - {x0: 1,x1: 2}
x1: 2
- champagne: Dom Perignon - champagne: Dom Perignon
coffee: Arabica coffee: Arabica
more: more:
@@ -2001,17 +1993,18 @@ bar2:
- Rochefort 10 - Rochefort 10
- Busch - Busch
- Leffe Rituel - Leffe Rituel
always: lots of water
- foo - foo
- bar - bar
- baz - baz
- bat - bat
)"); )");
// or nested:
ryml::parse_in_arena("[Kasteel Donker]", beer); ryml::parse_in_arena("[Kasteel Donker]", beer);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a
- b - b
- x0: 1 - {x0: 1,x1: 2}
x1: 2
- champagne: Dom Perignon - champagne: Dom Perignon
coffee: Arabica coffee: Arabica
more: more:
@@ -2022,6 +2015,7 @@ bar2:
- Busch - Busch
- Leffe Rituel - Leffe Rituel
- Kasteel Donker - Kasteel Donker
always: lots of water
- foo - foo
- bar - bar
- baz - baz
@@ -2037,7 +2031,8 @@ bar2:
* @see doc_parse */ * @see doc_parse */
void sample_parse_reuse_parser() void sample_parse_reuse_parser()
{ {
ryml::Parser parser; ryml::EventHandlerTree evt_handler = {};
ryml::Parser parser(&evt_handler);
// it is also advised to reserve the parser depth // it is also advised to reserve the parser depth
// to the expected depth of the data tree: // to the expected depth of the data tree:
@@ -2048,20 +2043,11 @@ void sample_parse_reuse_parser()
parser.reserve_stack(20); // But this will cause an allocation parser.reserve_stack(20); // But this will cause an allocation
// because it is above 16. // because it is above 16.
auto champagnes = parser.parse_in_arena("champagnes.yml", "[Dom Perignon, Gosset Grande Reserve, Ruinart Blanc de Blancs, Jacquesson 742]"); ryml::Tree champagnes = parse_in_arena(&parser, "champagnes.yml", "[Dom Perignon, Gosset Grande Reserve, Jacquesson 742]");
CHECK(ryml::emitrs_yaml<std::string>(champagnes) == R"(- Dom Perignon CHECK(ryml::emitrs_yaml<std::string>(champagnes) == "[Dom Perignon,Gosset Grande Reserve,Jacquesson 742]");
- Gosset Grande Reserve
- Ruinart Blanc de Blancs
- Jacquesson 742
)");
auto beers = parser.parse_in_arena("beers.yml", "[Rochefort 10, Busch, Leffe Rituel, Kasteel Donker]");
CHECK(ryml::emitrs_yaml<std::string>(beers) == R"(- Rochefort 10
- Busch
- Leffe Rituel
- Kasteel Donker
)");
ryml::Tree beers = parse_in_arena(&parser, "beers.yml", "[Rochefort 10, Busch, Leffe Rituel, Kasteel Donker]");
CHECK(ryml::emitrs_yaml<std::string>(beers) == "[Rochefort 10,Busch,Leffe Rituel,Kasteel Donker]");
} }
@@ -2073,13 +2059,14 @@ void sample_parse_reuse_parser()
void sample_parse_reuse_tree_and_parser() void sample_parse_reuse_tree_and_parser()
{ {
ryml::Tree tree; ryml::Tree tree;
ryml::Parser parser;
// it will always be faster if the tree's size is conveniently reserved: // it will always be faster if the tree's size is conveniently reserved:
tree.reserve(30); // reserve 30 nodes (good enough for this sample) tree.reserve(30); // reserve 30 nodes (good enough for this sample)
// if you are using the tree's arena to serialize data, // if you are using the tree's arena to serialize data,
// then reserve also the arena's size: // then reserve also the arena's size:
tree.reserve(256); // reserve 256 characters (good enough for this sample) tree.reserve(256); // reserve 256 characters (good enough for this sample)
ryml::EventHandlerTree evt_handler;
ryml::Parser parser(&evt_handler);
// it is also advised to reserve the parser depth // it is also advised to reserve the parser depth
// to the expected depth of the data tree: // to the expected depth of the data tree:
parser.reserve_stack(10); // the parser uses small storage parser.reserve_stack(10); // the parser uses small storage
@@ -2090,22 +2077,20 @@ void sample_parse_reuse_tree_and_parser()
parser.reserve_stack(20); // But this will cause an allocation parser.reserve_stack(20); // But this will cause an allocation
// because it is above 16. // because it is above 16.
ryml::csubstr champagnes = "[Dom Perignon, Gosset Grande Reserve, Ruinart Blanc de Blancs, Jacquesson 742]"; ryml::csubstr champagnes = "- Dom Perignon\n- Gosset Grande Reserve\n- Jacquesson 742";
ryml::csubstr beers = "[Rochefort 10, Busch, Leffe Rituel, Kasteel Donker]"; ryml::csubstr beers = "- Rochefort 10\n- Busch\n- Leffe Rituel\n- Kasteel Donker";
ryml::csubstr wines = "[Soalheiro, Niepoort Redoma 2017, Vina Esmeralda]"; ryml::csubstr wines = "- Soalheiro\n- Niepoort Redoma 2017\n- Vina Esmeralda";
parser.parse_in_arena("champagnes.yml", champagnes, &tree); parse_in_arena(&parser, "champagnes.yml", champagnes, &tree);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- Dom Perignon CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- Dom Perignon
- Gosset Grande Reserve - Gosset Grande Reserve
- Ruinart Blanc de Blancs
- Jacquesson 742 - Jacquesson 742
)"); )");
// watchout: this will APPEND to the given tree: // watchout: this will APPEND to the given tree:
parser.parse_in_arena("beers.yml", beers, &tree); parse_in_arena(&parser, "beers.yml", beers, &tree);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- Dom Perignon CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- Dom Perignon
- Gosset Grande Reserve - Gosset Grande Reserve
- Ruinart Blanc de Blancs
- Jacquesson 742 - Jacquesson 742
- Rochefort 10 - Rochefort 10
- Busch - Busch
@@ -2115,7 +2100,7 @@ void sample_parse_reuse_tree_and_parser()
// if you don't wish to append, clear the tree first: // if you don't wish to append, clear the tree first:
tree.clear(); tree.clear();
parser.parse_in_arena("wines.yml", wines, &tree); parse_in_arena(&parser, "wines.yml", wines, &tree);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- Soalheiro CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- Soalheiro
- Niepoort Redoma 2017 - Niepoort Redoma 2017
- Vina Esmeralda - Vina Esmeralda
@@ -2249,25 +2234,24 @@ void sample_create_trees()
xmas5["turtle-doves"] = "two"; xmas5["turtle-doves"] = "two";
root["cars"] = "GTO"; root["cars"] = "GTO";
std::cout << tree;
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(doe: 'a deer, a female deer' CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(doe: 'a deer, a female deer'
ray: a drop of golden sun ray: a drop of golden sun
pi: 3.14159 pi: 3.14159
xmas: true xmas: true
'french-hens': 3 french-hens: 3
'calling-birds': calling-birds:
- huey - huey
- dewey - dewey
- louie - louie
- fred - fred
'xmas-fifth-day': xmas-fifth-day:
'calling-birds': four calling-birds: four
'french-hens': 3 french-hens: 3
'golden-rings': 5 golden-rings: 5
partridges: partridges:
count: 1 count: 1
location: a pear tree location: a pear tree
'turtle-doves': two turtle-doves: two
cars: GTO cars: GTO
)"); )");
} }
@@ -2636,7 +2620,7 @@ str_tilde: ~
// As another example, nulity check based on the YAML nulity // As another example, nulity check based on the YAML nulity
// predicate: // predicate:
auto null_if_predicate = [](ryml::csubstr s) { auto null_if_predicate = [](ryml::csubstr s) {
return ryml::Tree::scalar_is_null(s) ? "null" : s; return ryml::scalar_is_null(s) ? "null" : s;
}; };
tree["empty_null"] << null_if_predicate(null); tree["empty_null"] << null_if_predicate(null);
tree["empty_nonnull"] << null_if_predicate(nonnull); tree["empty_nonnull"] << null_if_predicate(nonnull);
@@ -2652,7 +2636,7 @@ str_tilde: null
// As another example, nulity check based on the YAML nulity // As another example, nulity check based on the YAML nulity
// predicate, but returning "~" to simbolize nulity: // predicate, but returning "~" to simbolize nulity:
auto tilde_if_predicate = [](ryml::csubstr s) { auto tilde_if_predicate = [](ryml::csubstr s) {
return ryml::Tree::scalar_is_null(s) ? "~" : s; return ryml::scalar_is_null(s) ? "~" : s;
}; };
tree["empty_null"] << tilde_if_predicate(null); tree["empty_null"] << tilde_if_predicate(null);
tree["empty_nonnull"] << tilde_if_predicate(nonnull); tree["empty_nonnull"] << tilde_if_predicate(nonnull);
@@ -3118,11 +3102,6 @@ void sample_base64()
{{"The fool doth think he is wise, but the wise man knows himself to be a fool."}, {"VGhlIGZvb2wgZG90aCB0aGluayBoZSBpcyB3aXNlLCBidXQgdGhlIHdpc2UgbWFuIGtub3dzIGhpbXNlbGYgdG8gYmUgYSBmb29sLg=="}}, {{"The fool doth think he is wise, but the wise man knows himself to be a fool."}, {"VGhlIGZvb2wgZG90aCB0aGluayBoZSBpcyB3aXNlLCBidXQgdGhlIHdpc2UgbWFuIGtub3dzIGhpbXNlbGYgdG8gYmUgYSBmb29sLg=="}},
{{"Brevity is the soul of wit."}, {"QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu"}}, {{"Brevity is the soul of wit."}, {"QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu"}},
{{"All that glitters is not gold."}, {"QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu"}}, {{"All that glitters is not gold."}, {"QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu"}},
{{"These violent delights have violent ends..."}, {"VGhlc2UgdmlvbGVudCBkZWxpZ2h0cyBoYXZlIHZpb2xlbnQgZW5kcy4uLg=="}},
{{"How now, my love?"}, {"SG93IG5vdywgbXkgbG92ZT8="}},
{{"Why is your cheek so pale?"}, {"V2h5IGlzIHlvdXIgY2hlZWsgc28gcGFsZT8="}},
{{"How chance the roses there do fade so fast?"}, {"SG93IGNoYW5jZSB0aGUgcm9zZXMgdGhlcmUgZG8gZmFkZSBzbyBmYXN0Pw=="}},
{{"Belike for want of rain, which I could well beteem them from the tempest of my eyes."}, {"QmVsaWtlIGZvciB3YW50IG9mIHJhaW4sIHdoaWNoIEkgY291bGQgd2VsbCBiZXRlZW0gdGhlbSBmcm9tIHRoZSB0ZW1wZXN0IG9mIG15IGV5ZXMu"}},
}; };
// to encode base64 and write the result to val: // to encode base64 and write the result to val:
for(text_and_base64 c : cases) for(text_and_base64 c : cases)
@@ -3140,20 +3119,10 @@ void sample_base64()
'The fool doth think he is wise, but the wise man knows himself to be a fool.': VGhlIGZvb2wgZG90aCB0aGluayBoZSBpcyB3aXNlLCBidXQgdGhlIHdpc2UgbWFuIGtub3dzIGhpbXNlbGYgdG8gYmUgYSBmb29sLg== 'The fool doth think he is wise, but the wise man knows himself to be a fool.': VGhlIGZvb2wgZG90aCB0aGluayBoZSBpcyB3aXNlLCBidXQgdGhlIHdpc2UgbWFuIGtub3dzIGhpbXNlbGYgdG8gYmUgYSBmb29sLg==
Brevity is the soul of wit.: QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu Brevity is the soul of wit.: QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu
All that glitters is not gold.: QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu All that glitters is not gold.: QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu
These violent delights have violent ends...: VGhlc2UgdmlvbGVudCBkZWxpZ2h0cyBoYXZlIHZpb2xlbnQgZW5kcy4uLg==
'How now, my love?': SG93IG5vdywgbXkgbG92ZT8=
'Why is your cheek so pale?': V2h5IGlzIHlvdXIgY2hlZWsgc28gcGFsZT8=
'How chance the roses there do fade so fast?': SG93IGNoYW5jZSB0aGUgcm9zZXMgdGhlcmUgZG8gZmFkZSBzbyBmYXN0Pw==
'Belike for want of rain, which I could well beteem them from the tempest of my eyes.': QmVsaWtlIGZvciB3YW50IG9mIHJhaW4sIHdoaWNoIEkgY291bGQgd2VsbCBiZXRlZW0gdGhlbSBmcm9tIHRoZSB0ZW1wZXN0IG9mIG15IGV5ZXMu
TG92ZSBhbGwsIHRydXN0IGEgZmV3LCBkbyB3cm9uZyB0byBub25lLg==: 'Love all, trust a few, do wrong to none.' TG92ZSBhbGwsIHRydXN0IGEgZmV3LCBkbyB3cm9uZyB0byBub25lLg==: 'Love all, trust a few, do wrong to none.'
VGhlIGZvb2wgZG90aCB0aGluayBoZSBpcyB3aXNlLCBidXQgdGhlIHdpc2UgbWFuIGtub3dzIGhpbXNlbGYgdG8gYmUgYSBmb29sLg==: 'The fool doth think he is wise, but the wise man knows himself to be a fool.' VGhlIGZvb2wgZG90aCB0aGluayBoZSBpcyB3aXNlLCBidXQgdGhlIHdpc2UgbWFuIGtub3dzIGhpbXNlbGYgdG8gYmUgYSBmb29sLg==: 'The fool doth think he is wise, but the wise man knows himself to be a fool.'
QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu: Brevity is the soul of wit. QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu: Brevity is the soul of wit.
QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu: All that glitters is not gold. QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu: All that glitters is not gold.
VGhlc2UgdmlvbGVudCBkZWxpZ2h0cyBoYXZlIHZpb2xlbnQgZW5kcy4uLg==: These violent delights have violent ends...
SG93IG5vdywgbXkgbG92ZT8=: 'How now, my love?'
V2h5IGlzIHlvdXIgY2hlZWsgc28gcGFsZT8=: 'Why is your cheek so pale?'
SG93IGNoYW5jZSB0aGUgcm9zZXMgdGhlcmUgZG8gZmFkZSBzbyBmYXN0Pw==: 'How chance the roses there do fade so fast?'
QmVsaWtlIGZvciB3YW50IG9mIHJhaW4sIHdoaWNoIEkgY291bGQgd2VsbCBiZXRlZW0gdGhlbSBmcm9tIHRoZSB0ZW1wZXN0IG9mIG15IGV5ZXMu: 'Belike for want of rain, which I could well beteem them from the tempest of my eyes.'
)"); )");
char buf1_[128], buf2_[128]; char buf1_[128], buf2_[128];
ryml::substr buf1 = buf1_; // this is where we will write the result (using >>) ryml::substr buf1 = buf1_; // this is where we will write the result (using >>)
@@ -4251,16 +4220,16 @@ NodeOne:
)"); )");
// you can override the emit style of individual nodes: // you can override the emit style of individual nodes:
for(ryml::NodeRef child : tree["NodeOne"].children()) for(ryml::NodeRef child : tree["NodeOne"].children())
child |= ryml::_WIP_STYLE_FLOW_SL; // flow, single-line child |= ryml::FLOW_SL; // flow, single-line
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(NodeOne: CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(NodeOne:
- {key: a,desc: b,class: c,number: d} - {key: a,desc: b,class: c,number: d}
- {key: e,desc: f,class: g,number: h} - {key: e,desc: f,class: g,number: h}
- {key: i,desc: j,class: k,number: l} - {key: i,desc: j,class: k,number: l}
)"); )");
tree["NodeOne"] |= ryml::_WIP_STYLE_FLOW_SL; tree["NodeOne"] |= ryml::FLOW_SL;
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(NodeOne: [{key: a,desc: b,class: c,number: d},{key: e,desc: f,class: g,number: h},{key: i,desc: j,class: k,number: l}] CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(NodeOne: [{key: a,desc: b,class: c,number: d},{key: e,desc: f,class: g,number: h},{key: i,desc: j,class: k,number: l}]
)"); )");
tree.rootref() |= ryml::_WIP_STYLE_FLOW_SL; tree.rootref() |= ryml::FLOW_SL;
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"({NodeOne: [{key: a,desc: b,class: c,number: d},{key: e,desc: f,class: g,number: h},{key: i,desc: j,class: k,number: l}]})"); CHECK(ryml::emitrs_yaml<std::string>(tree) == R"({NodeOne: [{key: a,desc: b,class: c,number: d},{key: e,desc: f,class: g,number: h},{key: i,desc: j,class: k,number: l}]})");
} }
@@ -4271,22 +4240,22 @@ NodeOne:
/** shows how to parse and emit JSON. */ /** shows how to parse and emit JSON. */
void sample_json() void sample_json()
{ {
ryml::csubstr json = R"({
"doe":"a deer, a female deer",
"ray":"a drop of golden sun",
"me":"a name, I call myself",
"far":"a long long way to go"
})";
// Since JSON is a subset of YAML, parsing JSON is just the // Since JSON is a subset of YAML, parsing JSON is just the
// same as YAML: // same as YAML:
ryml::Tree tree = ryml::parse_in_arena(R"({ ryml::Tree tree = ryml::parse_in_arena(json);
"doe":"a deer, a female deer", // If you are sure the source is valid json, you can use the
"ray":"a drop of golden sun", // appropriate parse_json overload, which is faster because json
"me":"a name, I call myself", // has a smaller grammar:
"far":"a long long way to go" ryml::Tree json_tree = ryml::parse_json_in_arena(json);
})");
// However, emitting still defaults to YAML
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"('doe': 'a deer, a female deer'
'ray': 'a drop of golden sun'
'me': 'a name, I call myself'
'far': 'a long long way to go'
)");
// to emit JSON, use the proper overload: // to emit JSON, use the proper overload:
CHECK(ryml::emitrs_json<std::string>(tree) == R"({"doe": "a deer, a female deer","ray": "a drop of golden sun","me": "a name, I call myself","far": "a long long way to go"})"); CHECK(ryml::emitrs_json<std::string>(tree) == R"({"doe": "a deer, a female deer","ray": "a drop of golden sun","me": "a name, I call myself","far": "a long long way to go"})");
CHECK(ryml::emitrs_json<std::string>(json_tree) == R"({"doe": "a deer, a female deer","ray": "a drop of golden sun","me": "a name, I call myself","far": "a long long way to go"})");
// to emit JSON to a stream: // to emit JSON to a stream:
std::stringstream ss; std::stringstream ss;
ss << ryml::as_json(tree); // <- mark it like this ss << ryml::as_json(tree); // <- mark it like this
@@ -4300,7 +4269,8 @@ void sample_json()
// //
// - tags cannot be emitted as json, and are not allowed. // - tags cannot be emitted as json, and are not allowed.
// //
// - anchors and aliases cannot be emitted as json and are not allowed. // - anchors and references cannot be emitted as json and
// are not allowed.
} }
@@ -4344,7 +4314,30 @@ bill_to: &id001
state: KS state: KS
ship_to: *id001 ship_to: *id001
&keyref key: &valref val &keyref key: &valref val
*valref: *keyref *valref : *keyref
)";
std::string resolved = R"(base:
name: Everyone has same name
foo:
name: Everyone has same name
age: 10
bar:
name: Everyone has same name
age: 20
bill_to:
street: |-
123 Tornado Alley
Suite 16
city: East Centerville
state: KS
ship_to:
street: |-
123 Tornado Alley
Suite 16
city: East Centerville
state: KS
key: val
val: key
)"; )";
ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(unresolved)); ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(unresolved));
@@ -4381,40 +4374,96 @@ ship_to: *id001
void sample_tags() void sample_tags()
{ {
std::string unresolved = R"(--- !!map const std::string yaml = R"(--- !!map
a: 0 a: 0
b: 1 b: 1
--- !map --- !map
? a a: b
: b
--- !!seq --- !!seq
- a - a
- b - b
--- !!str
a
b
...
--- !!str a b --- !!str a b
... --- !!str 'a: b'
--- !!str a b
--- !!str
a: b
--- !!str a: b
--- ---
!!str a: b !!str a: b
--- --- !!set
!!str a
b
---
!!set
? a ? a
? b ? b
--- !!set --- !!set
? a a:
--- --- !!seq
[!!int 0, !!str 0] - !!int 0
- !!str 1
)"; )";
std::string resolved = R"(--- !!map const ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(yaml));
const ryml::ConstNodeRef root = tree.rootref();
CHECK(root.is_stream());
CHECK(root.num_children() == 9);
for(ryml::ConstNodeRef doc : root.children())
CHECK(doc.is_doc());
// tags are kept verbatim from the source:
CHECK(root[0].has_val_tag());
CHECK(root[0].val_tag() == "!!map"); // valid only if the node has a val tag
CHECK(root[1].val_tag() == "!map");
CHECK(root[2].val_tag() == "!!seq");
CHECK(root[3].val_tag() == "!!str");
CHECK(root[4].val_tag() == "!!str");
CHECK(root[5]["a"].has_key_tag());
CHECK(root[5]["a"].key_tag() == "!!str"); // valid only if the node has a key tag
CHECK(root[6].val_tag() == "!!set");
CHECK(root[7].val_tag() == "!!set");
CHECK(root[8].val_tag() == "!!seq");
CHECK(root[8][0].val_tag() == "!!int");
CHECK(root[8][1].val_tag() == "!!str");
// ryml also provides a complete toolbox to deal with tags.
// there is an enumeration for the standard YAML tags:
CHECK(ryml::to_tag("!map") == ryml::TAG_NONE);
CHECK(ryml::to_tag("!!map") == ryml::TAG_MAP);
CHECK(ryml::to_tag("!!seq") == ryml::TAG_SEQ);
CHECK(ryml::to_tag("!!str") == ryml::TAG_STR);
CHECK(ryml::to_tag("!!int") == ryml::TAG_INT);
CHECK(ryml::to_tag("!!set") == ryml::TAG_SET);
// given a tag enum, you can fetch the short tag string:
CHECK(ryml::from_tag(ryml::TAG_NONE) == "");
CHECK(ryml::from_tag(ryml::TAG_MAP) == "!!map");
CHECK(ryml::from_tag(ryml::TAG_SEQ) == "!!seq");
CHECK(ryml::from_tag(ryml::TAG_STR) == "!!str");
CHECK(ryml::from_tag(ryml::TAG_INT) == "!!int");
CHECK(ryml::from_tag(ryml::TAG_SET) == "!!set");
// you can also fetch the long tag string:
CHECK(ryml::from_tag_long(ryml::TAG_NONE) == "");
CHECK(ryml::from_tag_long(ryml::TAG_MAP) == "<tag:yaml.org,2002:map>");
CHECK(ryml::from_tag_long(ryml::TAG_SEQ) == "<tag:yaml.org,2002:seq>");
CHECK(ryml::from_tag_long(ryml::TAG_STR) == "<tag:yaml.org,2002:str>");
CHECK(ryml::from_tag_long(ryml::TAG_INT) == "<tag:yaml.org,2002:int>");
CHECK(ryml::from_tag_long(ryml::TAG_SET) == "<tag:yaml.org,2002:set>");
// and likewise:
CHECK(ryml::to_tag("!map") == ryml::TAG_NONE);
CHECK(ryml::to_tag("<tag:yaml.org,2002:map>") == ryml::TAG_MAP);
CHECK(ryml::to_tag("<tag:yaml.org,2002:seq>") == ryml::TAG_SEQ);
CHECK(ryml::to_tag("<tag:yaml.org,2002:str>") == ryml::TAG_STR);
CHECK(ryml::to_tag("<tag:yaml.org,2002:int>") == ryml::TAG_INT);
CHECK(ryml::to_tag("<tag:yaml.org,2002:set>") == ryml::TAG_SET);
// to normalize a tag as much as possible, use normalize_tag():
CHECK(ryml::normalize_tag("!!map") == "!!map");
CHECK(ryml::normalize_tag("!<tag:yaml.org,2002:map>") == "!!map");
CHECK(ryml::normalize_tag("<tag:yaml.org,2002:map>") == "!!map");
CHECK(ryml::normalize_tag("tag:yaml.org,2002:map") == "!!map");
CHECK(ryml::normalize_tag("!<!!map>") == "<!!map>");
CHECK(ryml::normalize_tag("!map") == "!map");
CHECK(ryml::normalize_tag("!my!foo") == "!my!foo");
// and also for the long form:
CHECK(ryml::normalize_tag_long("!!map") == "<tag:yaml.org,2002:map>");
CHECK(ryml::normalize_tag_long("!<tag:yaml.org,2002:map>") == "<tag:yaml.org,2002:map>");
CHECK(ryml::normalize_tag_long("<tag:yaml.org,2002:map>") == "<tag:yaml.org,2002:map>");
CHECK(ryml::normalize_tag_long("tag:yaml.org,2002:map") == "<tag:yaml.org,2002:map>");
CHECK(ryml::normalize_tag_long("!<!!map>") == "<!!map>");
CHECK(ryml::normalize_tag_long("!map") == "!map");
// The tree provides the following methods applying to every node
// with a key and/or val tag:
ryml::Tree normalized_tree = tree;
normalized_tree.normalize_tags(); // normalize all tags in short form
CHECK(ryml::emitrs_yaml<std::string>(normalized_tree) == R"(--- !!map
a: 0 a: 0
b: 1 b: 1
--- !map --- !map
@@ -4423,30 +4472,75 @@ a: b
- a - a
- b - b
--- !!str a b --- !!str a b
--- !!str a b
--- !!str a b
--- !!str 'a: b'
--- !!str 'a: b' --- !!str 'a: b'
--- ---
!!str a: b !!str a: b
--- !!str a b
--- !!set --- !!set
a: a:
b: b:
--- !!set --- !!set
a: a:
--- --- !!seq
- !!int 0 - !!int 0
- !!str 0 - !!str 1
)");
ryml::Tree normalized_tree_long = tree;
normalized_tree_long.normalize_tags_long(); // normalize all tags in short form
CHECK(ryml::emitrs_yaml<std::string>(normalized_tree_long) == R"(--- !<tag:yaml.org,2002:map>
a: 0
b: 1
--- !map
a: b
--- !<tag:yaml.org,2002:seq>
- a
- b
--- !<tag:yaml.org,2002:str> a b
--- !<tag:yaml.org,2002:str> 'a: b'
---
!<tag:yaml.org,2002:str> a: b
--- !<tag:yaml.org,2002:set>
a:
b:
--- !<tag:yaml.org,2002:set>
a:
--- !<tag:yaml.org,2002:seq>
- !<tag:yaml.org,2002:int> 0
- !<tag:yaml.org,2002:str> 1
)");
}
//-----------------------------------------------------------------------------
void sample_tag_directives()
{
const std::string yaml = R"(
%TAG !m! !my-
--- # Bulb here
!m!light fluorescent
...
%TAG !m! !meta-
--- # Color here
!m!light green
)"; )";
ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(unresolved)); ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(yaml));
CHECK(ryml::emitrs_yaml<std::string>(tree) == resolved); CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(%TAG !m! !my-
const ryml::ConstNodeRef stream = tree.rootref(); --- !m!light fluorescent
CHECK(stream.is_stream()); ...
CHECK(stream.num_children() == 13); %TAG !m! !meta-
for(auto node : stream.children()) --- !m!light green
CHECK(node.is_doc()); )");
CHECK(stream[11].val_tag() == "!!set"); // tags are not resolved by default. Use .resolve_tags() to
// accomplish this:
tree.resolve_tags();
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(%TAG !m! !my-
--- !<!my-light> fluorescent
...
%TAG !m! !meta-
--- !<!meta-light> green
)");
// see also tree.normalize_tags()
// see also tree.normalize_tags_long()
} }
@@ -4593,9 +4687,9 @@ void sample_error_handler()
ErrorHandlerExample errh; ErrorHandlerExample errh;
// set a global error handler. Note the error callback must never // set a global error handler. Note the error callback must never
// return: it must either abort or throw an exception. Otherwise, // return: it must either throw an exception, use setjmp() and
// the parser will enter into an infinite loop, or the program may // longjmp(), or abort. Otherwise, the parser will enter into an
// crash. // infinite loop, or the program may crash.
ryml::set_callbacks(errh.callbacks()); ryml::set_callbacks(errh.callbacks());
errh.check_effect(/*committed*/true); errh.check_effect(/*committed*/true);
CHECK(errh.check_error_occurs([&]{ CHECK(errh.check_error_occurs([&]{
@@ -4728,7 +4822,8 @@ void sample_global_allocator()
// verify that by reserving we save allocations // verify that by reserving we save allocations
{ {
ryml::Parser parser; // reuse a parser ryml::EventHandlerTree evt_handler;
ryml::Parser parser(&evt_handler); // reuse a parser
ryml::Tree tree; // reuse a tree ryml::Tree tree; // reuse a tree
tree.reserve(10); // reserve the number of nodes tree.reserve(10); // reserve the number of nodes
@@ -4744,7 +4839,7 @@ void sample_global_allocator()
// verify that no other allocations occur when parsing // verify that no other allocations occur when parsing
size_t size_before = mem.alloc_size; size_t size_before = mem.alloc_size;
parser.parse_in_arena("", R"([a, b, c, d, {foo: bar, money: pennys}])", &tree); parse_in_arena(&parser, "", R"([a, b, c, d, {foo: bar, money: pennys}])", &tree);
CHECK(mem.alloc_size == size_before); CHECK(mem.alloc_size == size_before);
CHECK(mem.num_allocs == 3); CHECK(mem.num_allocs == 3);
} }
@@ -4833,15 +4928,16 @@ void sample_per_tree_allocator()
{ {
// Watchout: ensure that the lifetime of the callbacks target // Watchout: ensure that the lifetime of the callbacks target
// exceeds the lifetime of the tree. // exceeds the lifetime of the tree.
auto parser = ryml::Parser(mrp.callbacks()); ryml::EventHandlerTree evt_handler(mrp.callbacks());
auto tree1 = ryml::Tree(mr1.callbacks()); ryml::Parser parser(&evt_handler);
auto tree2 = ryml::Tree(mr2.callbacks()); ryml::Tree tree1(mr1.callbacks());
ryml::Tree tree2(mr2.callbacks());
ryml::csubstr yml1 = "{a: b}"; ryml::csubstr yml1 = "{a: b}";
ryml::csubstr yml2 = "{c: d, e: f, g: [h, i, 0, 1, 2, 3]}"; ryml::csubstr yml2 = "{c: d, e: f, g: [h, i, 0, 1, 2, 3]}";
parser.parse_in_arena("file1.yml", yml1, &tree1); parse_in_arena(&parser, "file1.yml", yml1, &tree1);
parser.parse_in_arena("file2.yml", yml2, &tree2); parse_in_arena(&parser, "file2.yml", yml2, &tree2);
} }
CHECK(mrp.num_allocs == 0); // YAML depth not large enough to warrant a parser allocation CHECK(mrp.num_allocs == 0); // YAML depth not large enough to warrant a parser allocation
@@ -4894,7 +4990,8 @@ foo: [one, [two, three]]
// default. // default.
ryml::ParserOptions opts = {}; ryml::ParserOptions opts = {};
opts.locations(true); // enable locations, default is false opts.locations(true); // enable locations, default is false
ryml::Parser parser(opts); ryml::EventHandlerTree evt_handler = {};
ryml::Parser parser(&evt_handler, opts);
CHECK(parser.options().locations()); CHECK(parser.options().locations());
// When locations are enabled, the first task while parsing will // When locations are enabled, the first task while parsing will
// consist of building and caching (in the parser) a // consist of building and caching (in the parser) a
@@ -4910,8 +5007,8 @@ foo: [one, [two, three]]
// parsing: // parsing:
parser.reserve_locations(50u); // reserve for 50 lines parser.reserve_locations(50u); // reserve for 50 lines
// Now the structure will be built during parsing: // Now the structure will be built during parsing:
ryml::Tree tree = parser.parse_in_arena("source.yml", yaml); ryml::Tree tree = parse_in_arena(&parser, "source.yml", yaml);
// Now we are ready to query the location from the parser: // After this, we are ready to query the location from the parser:
ryml::Location loc = parser.location(tree.rootref()); ryml::Location loc = parser.location(tree.rootref());
// As for the complexity of the query: for large buffers it is // As for the complexity of the query: for large buffers it is
// O(log(numlines)). For short source buffers (30 lines and less), // O(log(numlines)). For short source buffers (30 lines and less),
@@ -4957,7 +5054,7 @@ foo: [one, [two, three]]
// the parser is reused, earlier trees will loose the possibility // the parser is reused, earlier trees will loose the possibility
// of querying for location. It is undefined behavior to query the // of querying for location. It is undefined behavior to query the
// parser for the location of a node from an earlier tree: // parser for the location of a node from an earlier tree:
ryml::Tree docval = parser.parse_in_arena("docval.yaml", "this is a docval"); ryml::Tree docval = parse_in_arena(&parser, "docval.yaml", "this is a docval");
// From now on, none of the locations from the previous tree can // From now on, none of the locations from the previous tree can
// be queried: // be queried:
//loc = parser.location(tree.rootref()); // ERROR, undefined behavior //loc = parser.location(tree.rootref()); // ERROR, undefined behavior
@@ -4967,7 +5064,7 @@ foo: [one, [two, three]]
CHECK(loc.col == 0u); CHECK(loc.col == 0u);
// NOTES ABOUT CONTAINER LOCATIONS // NOTES ABOUT CONTAINER LOCATIONS
ryml::Tree tree2 = parser.parse_in_arena("containers.yaml", R"( ryml::Tree tree2 = parse_in_arena(&parser, "containers.yaml", R"(
a new: buffer a new: buffer
to: be parsed to: be parsed
map with key: map with key:

View File

@@ -25,12 +25,24 @@ See also:
</Type> </Type>
<Type Name="c4::yml::NodeType"> <Type Name="c4::yml::NodeType">
<DisplayString>{type}</DisplayString> <DisplayString Condition="((type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((type &amp; c4::yml::VAL) == c4::yml::VAL)">[KEYVAL]</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((type &amp; c4::yml::SEQ) == c4::yml::SEQ)">[KEYSEQ]</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((type &amp; c4::yml::MAP) == c4::yml::MAP)">[KEYMAP]</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::DOC ) == c4::yml::DOC) &amp;&amp; ((type &amp; c4::yml::VAL) == c4::yml::VAL)">[DOCVAL]</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::DOC ) == c4::yml::DOC) &amp;&amp; ((type &amp; c4::yml::SEQ) == c4::yml::SEQ)">[DOCSEQ]</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::DOC ) == c4::yml::DOC) &amp;&amp; ((type &amp; c4::yml::MAP) == c4::yml::MAP)">[DOCMAP]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::VAL ) == c4::yml::VAL" >[VAL]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::KEY ) == c4::yml::KEY" >[KEY]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::SEQ ) == c4::yml::SEQ" >[SEQ]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::MAP ) == c4::yml::MAP" >[MAP]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::DOC ) == c4::yml::DOC" >[DOC]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::STREAM) == c4::yml::STREAM">[STREAM]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::NOTYPE) == c4::yml::NOTYPE">[NOTYPE]</DisplayString>
<Expand> <Expand>
<Synthetic Name="[enabled bits]"> <Synthetic Name="[type bits]">
<Expand> <Expand>
<Item Name="[0]" Condition="(type &amp; c4::yml::VAL) != 0">c4::yml::VAL</Item> <Item Name="[0]" Condition="(type &amp; c4::yml::KEY) != 0">c4::yml::KEY</Item>
<Item Name="[1]" Condition="(type &amp; c4::yml::KEY) != 0">c4::yml::KEY</Item> <Item Name="[1]" Condition="(type &amp; c4::yml::VAL) != 0">c4::yml::VAL</Item>
<Item Name="[2]" Condition="(type &amp; c4::yml::MAP) != 0">c4::yml::MAP</Item> <Item Name="[2]" Condition="(type &amp; c4::yml::MAP) != 0">c4::yml::MAP</Item>
<Item Name="[3]" Condition="(type &amp; c4::yml::SEQ) != 0">c4::yml::SEQ</Item> <Item Name="[3]" Condition="(type &amp; c4::yml::SEQ) != 0">c4::yml::SEQ</Item>
<Item Name="[4]" Condition="(type &amp; c4::yml::DOC) != 0">c4::yml::DOC</Item> <Item Name="[4]" Condition="(type &amp; c4::yml::DOC) != 0">c4::yml::DOC</Item>
@@ -41,8 +53,25 @@ See also:
<Item Name="[9]" Condition="(type &amp; c4::yml::VALANCH) != 0">c4::yml::VALANCH</Item> <Item Name="[9]" Condition="(type &amp; c4::yml::VALANCH) != 0">c4::yml::VALANCH</Item>
<Item Name="[10]" Condition="(type &amp; c4::yml::KEYTAG) != 0">c4::yml::KEYTAG</Item> <Item Name="[10]" Condition="(type &amp; c4::yml::KEYTAG) != 0">c4::yml::KEYTAG</Item>
<Item Name="[11]" Condition="(type &amp; c4::yml::VALTAG) != 0">c4::yml::VALTAG</Item> <Item Name="[11]" Condition="(type &amp; c4::yml::VALTAG) != 0">c4::yml::VALTAG</Item>
<Item Name="[12]" Condition="(type &amp; c4::yml::VALQUO) != 0">c4::yml::VALQUO</Item> </Expand>
<Item Name="[13]" Condition="(type &amp; c4::yml::KEYQUO) != 0">c4::yml::KEYQUO</Item> </Synthetic>
<Synthetic Name="[style bits]">
<Expand>
<Item Name="[0]" Condition="(type &amp; c4::yml::_WIP_KEY_UNFILT) != 0">c4::yml::_WIP_KEY_UNFILT</Item>
<Item Name="[1]" Condition="(type &amp; c4::yml::_WIP_VAL_UNFILT) != 0">c4::yml::_WIP_VAL_UNFILT</Item>
<Item Name="[2]" Condition="(type &amp; c4::yml::_WIP_STYLE_FLOW_SL) != 0">c4::yml::_WIP_STYLE_FLOW</Item>
<Item Name="[3]" Condition="(type &amp; c4::yml::_WIP_STYLE_FLOW_ML) != 0">c4::yml::_WIP_STYLE_FLOW</Item>
<Item Name="[4]" Condition="(type &amp; c4::yml::_WIP_STYLE_BLOCK) != 0">c4::yml::_WIP_STYLE_BLOCK</Item>
<Item Name="[5]" Condition="(type &amp; c4::yml::_WIP_KEY_LITERAL) != 0">c4::yml::_WIP_KEY_LITERAL</Item>
<Item Name="[6]" Condition="(type &amp; c4::yml::_WIP_VAL_LITERAL) != 0">c4::yml::_WIP_VAL_LITERAL</Item>
<Item Name="[7]" Condition="(type &amp; c4::yml::_WIP_KEY_FOLDED) != 0">c4::yml::_WIP_KEY_FOLDED</Item>
<Item Name="[8]" Condition="(type &amp; c4::yml::_WIP_VAL_FOLDED) != 0">c4::yml::_WIP_VAL_FOLDED</Item>
<Item Name="[9]" Condition="(type &amp; c4::yml::_WIP_KEY_SQUO) != 0">c4::yml::_WIP_KEY_SQUO</Item>
<Item Name="[10]" Condition="(type &amp; c4::yml::_WIP_VAL_SQUO) != 0">c4::yml::_WIP_VAL_SQUO</Item>
<Item Name="[11]" Condition="(type &amp; c4::yml::_WIP_KEY_DQUO) != 0">c4::yml::_WIP_KEY_DQUO</Item>
<Item Name="[12]" Condition="(type &amp; c4::yml::_WIP_VAL_DQUO) != 0">c4::yml::_WIP_VAL_DQUO</Item>
<Item Name="[13]" Condition="(type &amp; c4::yml::_WIP_KEY_PLAIN) != 0">c4::yml::_WIP_KEY_PLAIN</Item>
<Item Name="[14]" Condition="(type &amp; c4::yml::_WIP_VAL_PLAIN) != 0">c4::yml::_WIP_VAL_PLAIN</Item>
</Expand> </Expand>
</Synthetic> </Synthetic>
</Expand> </Expand>
@@ -216,4 +245,60 @@ See also:
</Expand> </Expand>
</Type> </Type>
<Type Name="c4::yml::detail::FilterProcessorSrcDst">
<DisplayString>src={src.str,[rpos]} dst={dst.str,[wpos]}</DisplayString>
<Expand>
<Item Name="[src]">src</Item>
<Item Name="[dst]">dst</Item>
<Item Name="[rpos]">rpos</Item>
<Item Name="[wpos]">wpos</Item>
<Synthetic Name="[read]">
<StringView>src.str,[rpos]</StringView>
<Expand>
<ArrayItems>
<Size>rpos</Size>
<ValuePointer>src.str</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::yml::detail::FilterProcessorInplace">
<DisplayString>src={src.str,[rpos]} dst={src.str,[wpos]}</DisplayString>
<Expand>
<Item Name="[rpos]">rpos</Item>
<Item Name="[wpos]">wpos</Item>
<Item Name="[wcap]">wcap</Item>
<Synthetic Name="[buf]">
<StringView>src.str,[wcap]</StringView>
<Expand>
<ArrayItems>
<Size>wcap</Size>
<ValuePointer>src.str</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
<Item Name="[src]">src</Item>
<Synthetic Name="[to be read]">
<StringView>src.str+rpos,[src.len-rpos]</StringView>
<Expand>
<ArrayItems><Size>src.len-rpos</Size><ValuePointer>src.str+rpos</ValuePointer></ArrayItems>
</Expand>
</Synthetic>
<Synthetic Name="[read]">
<StringView>src.str,[rpos]</StringView>
<Expand>
<ArrayItems><Size>rpos</Size><ValuePointer>src.str</ValuePointer></ArrayItems>
</Expand>
</Synthetic>
<Synthetic Name="[written]">
<StringView>src.str,[wpos]</StringView>
<Expand>
<ArrayItems><Size>wpos</Size><ValuePointer>src.str</ValuePointer></ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>
</AutoVisualizer> </AutoVisualizer>

View File

@@ -5,7 +5,11 @@ c4_add_executable(ryml-parse-emit
FOLDER tools) FOLDER tools)
c4_add_executable(ryml-yaml-events c4_add_executable(ryml-yaml-events
SOURCES yaml_events.cpp ../test/test_suite/test_suite_events.hpp ../test/test_suite/test_suite_events_emitter.cpp SOURCES yaml_events.cpp
../test/test_suite/test_suite_events.hpp
../test/test_suite/test_suite_events_emitter.cpp
../test/test_suite/test_suite_event_handler.hpp
../test/test_suite/test_suite_event_handler.cpp
INC_DIRS ../test INC_DIRS ../test
LIBS ryml c4fs LIBS ryml c4fs
FOLDER tools) FOLDER tools)

View File

@@ -3,10 +3,14 @@
#else #else
#include <c4/yml/std/std.hpp> #include <c4/yml/std/std.hpp>
#include <c4/yml/parse.hpp> #include <c4/yml/parse.hpp>
#include <c4/yml/event_handler_tree.hpp>
#include <c4/yml/parse_engine.def.hpp>
#endif #endif
#include <test_suite/test_suite_events.hpp> #include <test_suite/test_suite_events.hpp>
#include <test_suite/test_suite_event_handler.hpp>
#include <c4/fs/fs.hpp> #include <c4/fs/fs.hpp>
#include <cstdio> #include <cstdio>
#ifdef C4_EXCEPTIONS #ifdef C4_EXCEPTIONS
#include <stdexcept> #include <stdexcept>
#else #else
@@ -15,46 +19,80 @@ std::jmp_buf jmp_env = {};
c4::csubstr jmp_msg = {}; c4::csubstr jmp_msg = {};
#endif #endif
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
//-----------------------------------------------------------------------------
const char usage[] = R"(usage:
ryml-yaml-events [-s|-t] [-] # read from stdin (default)
ryml-yaml-events [-s|-t] <file> # read from file
The option can be one of the following:
-s emit events from source: parse the YAML source,
and directly emit events during the parse (ie, no
ryml tree is created). This is the default behavior
when the option is omitted.
-t events from tree: parse the YAML source, creating a
ryml tree. Once the tree is completely created, emit
the events from the created tree.
When the option is omitted, -s is assumed.
EXAMPLES:
$ ryml-yaml-events # emit events direct from stdin
$ ryml-yaml-events - # emit events direct from stdin
$ ryml-yaml-events -s - # emit events direct from stdin
$ ryml-yaml-events -t # parse stdin to tree, emit events from created tree
$ ryml-yaml-events -t - # parse stdin to tree, emit events from created tree
$ ryml-yaml-events <file> # emit events direct from file
$ ryml-yaml-events - <file> # emit events direct from file
$ ryml-yaml-events -s <file> # emit events direct from file
$ ryml-yaml-events -t <file> # parse file to tree, emit events from created tree
)";
using namespace c4; using namespace c4;
using namespace c4::yml; using namespace c4::yml;
void usage(const char *exename); struct Args
std::string load_file(csubstr filename); {
void report_error(const char* msg, size_t length, Location loc, FILE *f); csubstr filename = "-";
bool events_from_tree = false;
static bool parse(Args *args, int argc, const char *argv[]);
};
//-----------------------------------------------------------------------------
std::string load_file(csubstr filename);
std::string emit_events_from_tree(csubstr filename, substr filecontents);
std::string emit_events_direct(csubstr filename, substr filecontents);
Callbacks create_custom_callbacks();
//-----------------------------------------------------------------------------
int main(int argc, const char *argv[]) int main(int argc, const char *argv[])
{ {
if(argc < 2) Args args = {};
{ if(!Args::parse(&args, argc, argv))
usage(argv[0]);
return 1; return 1;
} set_callbacks(create_custom_callbacks());
Callbacks callbacks = {};
pfn_error error = [](const char *msg, size_t msg_len, Location location, void *)
{
report_error(msg, msg_len, location, stderr);
C4_IF_EXCEPTIONS(
throw std::runtime_error({msg, msg_len});
C4_UNREACHABLE_AFTER_ERR();
,
jmp_msg.assign(msg, msg_len);
std::longjmp(jmp_env, 1);
);
};
callbacks.m_error = error;
set_callbacks(callbacks);
C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0)) C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0))
{ {
Tree tree(callbacks); std::string src = load_file(args.filename);
csubstr filename = to_csubstr(argv[1]); const std::string events = args.events_from_tree ?
std::string evt, src = load_file(filename); emit_events_from_tree(args.filename, to_substr(src))
tree.reserve(to_substr(src).count('\n')); :
parse_in_place(filename, to_substr(src), &tree); emit_events_direct(args.filename, to_substr(src));
emit_events(&evt, tree); std::fwrite(events.data(), 1, events.size(), stdout);
std::fwrite(evt.data(), 1, evt.size(), stdout);
} }
C4_IF_EXCEPTIONS_(catch(std::exception const&), else) C4_IF_EXCEPTIONS_(catch(std::exception const&), else)
{ {
@@ -66,14 +104,54 @@ int main(int argc, const char *argv[])
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void usage(const char *exename) std::string emit_events_from_tree(csubstr filename, substr filecontents)
{ {
std::printf(R"(usage: Tree tree(create_custom_callbacks());
%s - # read from stdin tree.reserve(filecontents.count('\n'));
%s <file> # read from file parse_in_place(filename, filecontents, &tree);
)", exename, exename); return emit_events_from_tree<std::string>(tree);
} }
std::string emit_events_direct(csubstr filename, substr filecontents)
{
EventHandlerYamlStd::EventSink sink = {};
EventHandlerYamlStd handler(&sink, create_custom_callbacks());
ParseEngine<EventHandlerYamlStd> parser(&handler);
parser.parse_in_place_ev(filename, filecontents);
return sink.result;
}
//-----------------------------------------------------------------------------
bool Args::parse(Args *args, int argc, const char *argv[])
{
if(argc > 3)
{
std::printf(usage);
return false;
}
*args = {};
if(argc == 3)
{
args->events_from_tree = (to_csubstr(argv[1]) == "-t");
args->filename = to_csubstr(argv[2]);
}
else if(argc == 2)
{
csubstr a = to_csubstr(argv[1]);
if(a == "-t")
args->events_from_tree = true;
else if(a == "-s")
args->events_from_tree = false;
else
args->filename = a;
}
return true;
}
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
std::string load_file(csubstr filename) std::string load_file(csubstr filename)
{ {
if(filename == "-") // read from stdin if(filename == "-") // read from stdin
@@ -87,7 +165,11 @@ std::string load_file(csubstr filename)
} }
return buf; return buf;
} }
C4_CHECK_MSG(fs::path_exists(filename.str), "cannot find file: %s (cwd=%s)", filename.str, fs::cwd<std::string>().c_str()); if(!fs::path_exists(filename.str))
{
std::fprintf(stderr, "cannot find file: %s (cwd=%s)\n", filename.str, fs::cwd<std::string>().c_str());
error("file not found");
}
return fs::file_get_contents<std::string>(filename.str); return fs::file_get_contents<std::string>(filename.str);
} }
@@ -109,3 +191,20 @@ void report_error(const char* msg, size_t length, Location loc, FILE *f)
} }
C4_SUPPRESS_WARNING_GCC_CLANG_POP C4_SUPPRESS_WARNING_GCC_CLANG_POP
Callbacks create_custom_callbacks()
{
Callbacks callbacks = {};
callbacks.m_error = [](const char *msg, size_t msg_len, Location location, void *)
{
report_error(msg, msg_len, location, stderr);
C4_IF_EXCEPTIONS(
throw std::runtime_error({msg, msg_len});
,
jmp_msg.assign(msg, msg_len);
std::longjmp(jmp_env, 1);
);
};
return callbacks;
}