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/
.python-version
compile_commands.json
launch.json
# test files
/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_anchors_and_aliases(); ///< deal with YAML anchors and aliases
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_error_handler(); ///< set a custom error handler
void sample_global_allocator(); ///< set a global allocator for ryml
@@ -106,6 +107,7 @@ int main()
sample::sample_json();
sample::sample_anchors_and_aliases();
sample::sample_tags();
sample::sample_tag_directives();
sample::sample_docs();
sample::sample_error_handler();
sample::sample_global_allocator();
@@ -209,12 +211,13 @@ struct CheckPredicate
{
const char *file;
const int line;
void operator() (bool predicate) const
{
if (!report_check(line, nullptr, predicate))
{
#ifdef RYML_DBG
RYML_DEBUG_BREAK();
#endif
}
}
};
@@ -296,22 +299,16 @@ void sample_lightning_overview()
// emit tree
// to std::string
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(foo: 1
bar:
- 10
- 11
- 12
john: doe
)");
std::cout << tree; // emit to stdout
ryml::emit_yaml(tree, stdout); // emit to file
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"({foo: 1,bar: [10,11,12],john: doe})");
std::cout << tree; // emit to ostream
ryml::emit_yaml(tree, stdout); // emit to FILE*
// emit node
ryml::ConstNodeRef foo = tree["foo"];
// to std::string
CHECK(ryml::emitrs_yaml<std::string>(foo) == "foo: 1\n");
std::cout << foo; // emit node to stdout
ryml::emit_yaml(foo, stdout); // emit node to file
std::cout << foo; // emit node to ostream
ryml::emit_yaml(foo, stdout); // emit node to FILE*
}
@@ -321,7 +318,10 @@ john: doe
void sample_quick_overview()
{
// 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);
// The resulting tree contains only views to the parsed string. If
@@ -797,11 +797,7 @@ void sample_quick_overview()
// Emitting:
ryml::csubstr expected_result = R"(foo: says who
bar:
- 20
- 30
- oh so nice
- oh so nice (serialized)
bar: [20,30,oh so nice,oh so nice (serialized)]
john: in_scope
float: 2.4
digits: 2.400000
@@ -871,21 +867,20 @@ I am something: indeed
// Getting the location of nodes in the source:
//
// 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:
ryml::Tree tree2;
parser.parse_in_arena("expected.yml", expected_result, &tree2);
ryml::Tree tree2 = parse_in_arena(&parser, "expected.yml", expected_result);
// ... and use it when querying
ryml::ConstNodeRef subject_node = tree2["bar"][1];
CHECK(subject_node.val() == "30");
ryml::Location loc = parser.location(subject_node);
CHECK(parser.location_contents(loc).begins_with("30"));
CHECK(loc.line == 3u);
CHECK(loc.col == 4u);
CHECK(loc.line == 1u);
CHECK(loc.col == 9u);
// For further details in location tracking,
// refer to the sample function below.
//------------------------------------------------------------------
// Dealing with UTF8
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
// 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";
ryml::csubstr s = ryml::to_csubstr(foobar_str);
@@ -989,8 +984,10 @@ void sample_substr()
// construct from a std::string: same approach as above.
// requires inclusion of the <ryml/std/string.hpp> header
// 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";
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
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::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["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
// arena, and then parse the copied buffer:
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["foo"].is_keyval());
@@ -1891,7 +1888,10 @@ void sample_parse_reuse_tree()
tree.reserve_arena(256); // reserve 256 characters (good enough for this sample)
// 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();
CHECK(root.num_children() == 2);
@@ -1905,21 +1905,15 @@ void sample_parse_reuse_tree()
CHECK(root["bar"][0].val() == "2");
CHECK(root["bar"][1].val() == "3");
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(foo: 1
bar:
- 2
- 3
bar: [2,3]
)");
// WATCHOUT: parsing into an existing tree will APPEND to it:
ryml::parse_in_arena("{foo2: 12, bar2: [22, 32]}", &tree);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(foo: 1
bar:
- 2
- 3
bar: [2,3]
foo2: 12
bar2:
- 22
- 32
bar2: [22,32]
)");
CHECK(root.num_children() == 4);
CHECK(root["foo2"].is_keyval());
@@ -1934,12 +1928,8 @@ bar2:
// clear first before parsing into an existing tree.
tree.clear();
tree.clear_arena(); // you may or may not want to clear the arena
ryml::parse_in_arena("[a, b, {x0: 1, x1: 2}]", &tree);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a
- b
- x0: 1
x1: 2
)");
ryml::parse_in_arena("- a\n- b\n- {x0: 1, x1: 2}", &tree);
CHECK(ryml::emitrs_yaml<std::string>(tree) == "- a\n- b\n- {x0: 1,x1: 2}\n");
CHECK(root.is_seq());
CHECK(root[0].val() == "a");
CHECK(root[1].val() == "b");
@@ -1949,11 +1939,10 @@ bar2:
// we can parse directly into a node nested deep in an existing tree:
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
- b
- x0: 1
x1: 2
- {x0: 1,x1: 2}
- champagne: Dom Perignon
coffee: Arabica
)");
@@ -1967,15 +1956,17 @@ bar2:
CHECK(root[3]["champagne"].val() == "Dom Perignon");
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 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("[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
- b
- x0: 1
x1: 2
- {x0: 1,x1: 2}
- champagne: Dom Perignon
coffee: Arabica
more:
@@ -1985,13 +1976,14 @@ bar2:
- Rochefort 10
- Busch
- 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
- b
- x0: 1
x1: 2
- {x0: 1,x1: 2}
- champagne: Dom Perignon
coffee: Arabica
more:
@@ -2001,17 +1993,18 @@ bar2:
- Rochefort 10
- Busch
- Leffe Rituel
always: lots of water
- foo
- bar
- baz
- bat
)");
// or nested:
ryml::parse_in_arena("[Kasteel Donker]", beer);
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(- a
- b
- x0: 1
x1: 2
- {x0: 1,x1: 2}
- champagne: Dom Perignon
coffee: Arabica
more:
@@ -2022,6 +2015,7 @@ bar2:
- Busch
- Leffe Rituel
- Kasteel Donker
always: lots of water
- foo
- bar
- baz
@@ -2037,7 +2031,8 @@ bar2:
* @see doc_parse */
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
// 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
// because it is above 16.
auto champagnes = parser.parse_in_arena("champagnes.yml", "[Dom Perignon, Gosset Grande Reserve, Ruinart Blanc de Blancs, Jacquesson 742]");
CHECK(ryml::emitrs_yaml<std::string>(champagnes) == R"(- Dom Perignon
- 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 champagnes = parse_in_arena(&parser, "champagnes.yml", "[Dom Perignon, Gosset Grande Reserve, Jacquesson 742]");
CHECK(ryml::emitrs_yaml<std::string>(champagnes) == "[Dom Perignon,Gosset Grande Reserve,Jacquesson 742]");
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()
{
ryml::Tree tree;
ryml::Parser parser;
// it will always be faster if the tree's size is conveniently reserved:
tree.reserve(30); // reserve 30 nodes (good enough for this sample)
// if you are using the tree's arena to serialize data,
// then reserve also the arena's size:
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
// to the expected depth of the data tree:
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
// because it is above 16.
ryml::csubstr champagnes = "[Dom Perignon, Gosset Grande Reserve, Ruinart Blanc de Blancs, Jacquesson 742]";
ryml::csubstr beers = "[Rochefort 10, Busch, Leffe Rituel, Kasteel Donker]";
ryml::csubstr wines = "[Soalheiro, Niepoort Redoma 2017, Vina Esmeralda]";
ryml::csubstr champagnes = "- Dom Perignon\n- Gosset Grande Reserve\n- Jacquesson 742";
ryml::csubstr beers = "- Rochefort 10\n- Busch\n- Leffe Rituel\n- Kasteel Donker";
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
- Gosset Grande Reserve
- Ruinart Blanc de Blancs
- Jacquesson 742
)");
// 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
- Gosset Grande Reserve
- Ruinart Blanc de Blancs
- Jacquesson 742
- Rochefort 10
- Busch
@@ -2115,7 +2100,7 @@ void sample_parse_reuse_tree_and_parser()
// if you don't wish to append, clear the tree first:
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
- Niepoort Redoma 2017
- Vina Esmeralda
@@ -2249,25 +2234,24 @@ void sample_create_trees()
xmas5["turtle-doves"] = "two";
root["cars"] = "GTO";
std::cout << tree;
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(doe: 'a deer, a female deer'
ray: a drop of golden sun
pi: 3.14159
xmas: true
'french-hens': 3
'calling-birds':
french-hens: 3
calling-birds:
- huey
- dewey
- louie
- fred
'xmas-fifth-day':
'calling-birds': four
'french-hens': 3
'golden-rings': 5
xmas-fifth-day:
calling-birds: four
french-hens: 3
golden-rings: 5
partridges:
count: 1
location: a pear tree
'turtle-doves': two
turtle-doves: two
cars: GTO
)");
}
@@ -2636,7 +2620,7 @@ str_tilde: ~
// As another example, nulity check based on the YAML nulity
// predicate:
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_nonnull"] << null_if_predicate(nonnull);
@@ -2652,7 +2636,7 @@ str_tilde: null
// As another example, nulity check based on the YAML nulity
// predicate, but returning "~" to simbolize nulity:
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_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=="}},
{{"Brevity is the soul of wit."}, {"QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu"}},
{{"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:
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==
Brevity is the soul of wit.: QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu
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.'
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.
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];
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:
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:
- {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["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}]
)");
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}]})");
}
@@ -4271,22 +4240,22 @@ NodeOne:
/** shows how to parse and emit 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
// same as YAML:
ryml::Tree tree = ryml::parse_in_arena(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"
})");
// 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'
)");
ryml::Tree tree = ryml::parse_in_arena(json);
// If you are sure the source is valid json, you can use the
// appropriate parse_json overload, which is faster because json
// has a smaller grammar:
ryml::Tree json_tree = ryml::parse_json_in_arena(json);
// 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>(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:
std::stringstream ss;
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.
//
// - 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
ship_to: *id001
&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));
@@ -4381,40 +4374,96 @@ ship_to: *id001
void sample_tags()
{
std::string unresolved = R"(--- !!map
const std::string yaml = R"(--- !!map
a: 0
b: 1
--- !map
? a
: b
a: b
--- !!seq
- 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
? b
--- !!set
? a
---
[!!int 0, !!str 0]
a:
--- !!seq
- !!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
b: 1
--- !map
@@ -4423,30 +4472,75 @@ a: b
- a
- b
--- !!str a b
--- !!str a b
--- !!str a b
--- !!str 'a: b'
--- !!str 'a: b'
---
!!str a: b
--- !!str a b
--- !!set
a:
b:
--- !!set
a:
---
--- !!seq
- !!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));
CHECK(ryml::emitrs_yaml<std::string>(tree) == resolved);
const ryml::ConstNodeRef stream = tree.rootref();
CHECK(stream.is_stream());
CHECK(stream.num_children() == 13);
for(auto node : stream.children())
CHECK(node.is_doc());
CHECK(stream[11].val_tag() == "!!set");
ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(yaml));
CHECK(ryml::emitrs_yaml<std::string>(tree) == R"(%TAG !m! !my-
--- !m!light fluorescent
...
%TAG !m! !meta-
--- !m!light green
)");
// 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;
// set a global error handler. Note the error callback must never
// return: it must either abort or throw an exception. Otherwise,
// the parser will enter into an infinite loop, or the program may
// crash.
// return: it must either throw an exception, use setjmp() and
// longjmp(), or abort. Otherwise, the parser will enter into an
// infinite loop, or the program may crash.
ryml::set_callbacks(errh.callbacks());
errh.check_effect(/*committed*/true);
CHECK(errh.check_error_occurs([&]{
@@ -4728,7 +4822,8 @@ void sample_global_allocator()
// 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
tree.reserve(10); // reserve the number of nodes
@@ -4744,7 +4839,7 @@ void sample_global_allocator()
// verify that no other allocations occur when parsing
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.num_allocs == 3);
}
@@ -4833,15 +4928,16 @@ void sample_per_tree_allocator()
{
// Watchout: ensure that the lifetime of the callbacks target
// exceeds the lifetime of the tree.
auto parser = ryml::Parser(mrp.callbacks());
auto tree1 = ryml::Tree(mr1.callbacks());
auto tree2 = ryml::Tree(mr2.callbacks());
ryml::EventHandlerTree evt_handler(mrp.callbacks());
ryml::Parser parser(&evt_handler);
ryml::Tree tree1(mr1.callbacks());
ryml::Tree tree2(mr2.callbacks());
ryml::csubstr yml1 = "{a: b}";
ryml::csubstr yml2 = "{c: d, e: f, g: [h, i, 0, 1, 2, 3]}";
parser.parse_in_arena("file1.yml", yml1, &tree1);
parser.parse_in_arena("file2.yml", yml2, &tree2);
parse_in_arena(&parser, "file1.yml", yml1, &tree1);
parse_in_arena(&parser, "file2.yml", yml2, &tree2);
}
CHECK(mrp.num_allocs == 0); // YAML depth not large enough to warrant a parser allocation
@@ -4894,7 +4990,8 @@ foo: [one, [two, three]]
// default.
ryml::ParserOptions opts = {};
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());
// When locations are enabled, the first task while parsing will
// consist of building and caching (in the parser) a
@@ -4910,8 +5007,8 @@ foo: [one, [two, three]]
// parsing:
parser.reserve_locations(50u); // reserve for 50 lines
// Now the structure will be built during parsing:
ryml::Tree tree = parser.parse_in_arena("source.yml", yaml);
// Now we are ready to query the location from the parser:
ryml::Tree tree = parse_in_arena(&parser, "source.yml", yaml);
// After this, we are ready to query the location from the parser:
ryml::Location loc = parser.location(tree.rootref());
// As for the complexity of the query: for large buffers it is
// 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
// of querying for location. It is undefined behavior to query the
// 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
// be queried:
//loc = parser.location(tree.rootref()); // ERROR, undefined behavior
@@ -4967,7 +5064,7 @@ foo: [one, [two, three]]
CHECK(loc.col == 0u);
// 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
to: be parsed
map with key:

View File

@@ -25,12 +25,24 @@ See also:
</Type>
<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>
<Synthetic Name="[enabled bits]">
<Synthetic Name="[type bits]">
<Expand>
<Item Name="[0]" Condition="(type &amp; c4::yml::VAL) != 0">c4::yml::VAL</Item>
<Item Name="[1]" Condition="(type &amp; c4::yml::KEY) != 0">c4::yml::KEY</Item>
<Item Name="[0]" 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="[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>
@@ -41,8 +53,25 @@ See also:
<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="[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>
<Item Name="[13]" Condition="(type &amp; c4::yml::KEYQUO) != 0">c4::yml::KEYQUO</Item>
</Expand>
</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>
</Synthetic>
</Expand>
@@ -216,4 +245,60 @@ See also:
</Expand>
</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>

View File

@@ -5,7 +5,11 @@ c4_add_executable(ryml-parse-emit
FOLDER tools)
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
LIBS ryml c4fs
FOLDER tools)

View File

@@ -3,10 +3,14 @@
#else
#include <c4/yml/std/std.hpp>
#include <c4/yml/parse.hpp>
#include <c4/yml/event_handler_tree.hpp>
#include <c4/yml/parse_engine.def.hpp>
#endif
#include <test_suite/test_suite_events.hpp>
#include <test_suite/test_suite_event_handler.hpp>
#include <c4/fs/fs.hpp>
#include <cstdio>
#ifdef C4_EXCEPTIONS
#include <stdexcept>
#else
@@ -15,46 +19,80 @@ std::jmp_buf jmp_env = {};
c4::csubstr jmp_msg = {};
#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::yml;
void usage(const char *exename);
std::string load_file(csubstr filename);
void report_error(const char* msg, size_t length, Location loc, FILE *f);
struct Args
{
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[])
{
if(argc < 2)
{
usage(argv[0]);
Args args = {};
if(!Args::parse(&args, argc, argv))
return 1;
}
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);
set_callbacks(create_custom_callbacks());
C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0))
{
Tree tree(callbacks);
csubstr filename = to_csubstr(argv[1]);
std::string evt, src = load_file(filename);
tree.reserve(to_substr(src).count('\n'));
parse_in_place(filename, to_substr(src), &tree);
emit_events(&evt, tree);
std::fwrite(evt.data(), 1, evt.size(), stdout);
std::string src = load_file(args.filename);
const std::string events = args.events_from_tree ?
emit_events_from_tree(args.filename, to_substr(src))
:
emit_events_direct(args.filename, to_substr(src));
std::fwrite(events.data(), 1, events.size(), stdout);
}
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:
%s - # read from stdin
%s <file> # read from file
)", exename, exename);
Tree tree(create_custom_callbacks());
tree.reserve(filecontents.count('\n'));
parse_in_place(filename, filecontents, &tree);
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)
{
if(filename == "-") // read from stdin
@@ -87,7 +165,11 @@ std::string load_file(csubstr filename)
}
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);
}
@@ -109,3 +191,20 @@ void report_error(const char* msg, size_t length, Location loc, FILE *f)
}
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;
}