Rapid YAML - a library to parse and emit YAML, and do it fast.
MIT License
Bot releases are visible (Hide)
ConstNodeRef
to hold a constant reference to a node. As the name implies, a ConstNodeRef
object cannot be used in any tree-mutating operation. It is also smaller than the existing NodeRef
, and faster because it does not need to check its own validity on every access. As a result of this change, there are now some constraints when obtaining a ref from a tree, and existing code is likely to break in this type of situation:
const Tree const_tree = ...;
NodeRef nr = const_tree.rootref(); // ERROR (was ok): cannot obtain a mutating NodeRef from a const Tree
ConstNodeRef cnr = const_tree.rootref(); // ok
Tree tree = ...;
NodeRef nr = tree.rootref(); // ok
ConstNodeRef cnr = tree.rootref(); // ok (implicit conversion from NodeRef to ConstNodeRef)
// to obtain a ConstNodeRef from a mutable Tree
// while avoiding implicit conversion, use the `c`
// prefix:
ConstNodeRef cnr = tree.crootref();
// likewise for tree.ref() and tree.cref().
nr = cnr; // ERROR: cannot obtain NodeRef from ConstNodeRef
cnr = nr; // ok
The use of ConstNodeRef
also needs to be propagated through client code. One such place is when deserializing types:
// needs to be changed from:
template<class T> bool read(ryml::NodeRef const& n, T *var);
// ... to:
template<class T> bool read(ryml::ConstNodeRef const& n, T *var);
ConstNodeRef/NodeRef
had the problem that const methods in the CRTP base did not participate in overload resolution (#294), preventing calls from const NodeRef
objects. This was fixed by moving non-const methods to the CRTP base and disabling them with SFINAE (PR#295)..cbegin()
, .cend()
, .cchildren()
, .csiblings()
(PR#295).emit()
and emitrs()
(#120, PR#303): use emit_yaml()
and emitrs_yaml()
instead. This was done to improve compatibility with Qt, which leaks a macro named emit
. For more information, see #120.
emit()
, add emit_yaml()
and emit_json()
.compute_emit_length()
, add compute_emit_yaml_length()
and compute_emit_json_length()
.emit_in_place()
, add emit_yaml_in_place()
and emit_json_in_place()
.Parser parser(ParserOptions().locations(true));
// now parsing also builds location lookup:
Tree t = parser.parse_in_arena("myfile.yml", "foo: bar");
assert(parser.location(t["foo"]).line == 0u);
Parser parser;
assert(parser.options().locations() == false);
Tree::arena_pos()
: use Tree::arena_size()
instead (PR#290).has_siblings()
: use Tree::has_other_siblings()
instead (PR#330.Improve performance of integer serialization and deserialization (in c4core). Eg, on Linux/g++11.2, with integral types:
c4::to_chars()
can be expected to be roughly...
std::to_chars()
sprintf()
stringstream::operator<<()
followed by stringstream::str()
c4::from_chars()
can be expected to be roughly...
std::from_chars()
scanf()
stringstream::str()
followed by stringstream::operator>>()
Fix #289 and #331 - parsing of single-line flow-style sequences had quadratic complexity, causing long parse times in ultra long lines PR#293/PR#332.
:
before scanning for ,
or ]
, which caused line-length scans on every scalar scan. Changing the order of the checks was enough to address the quadratic complexity, and the parse times for flow-style are now in line with block-style.Parser::_scan_scalar()
into several different {seq,map}x{block,flow}
functions specific for each context. Expect some improvement in parse times.Tree::has_child()
in Tree::insert_child()
that caused quadratic behavior because the assertion had linear complexity. It was replaced with a somewhat equivalent O(1) assertion.Container | Style | 10elms | 100elms | 1000elms |
---|---|---|---|---|
1000 Maps | block | 50.8MB/s | 57.8MB/s | 63.9MB/s |
1000 Maps | flow | 58.2MB/s | 65.9MB/s | 74.5MB/s |
1000 Seqs | block | 55.7MB/s | 59.2MB/s | 60.0MB/s |
1000 Seqs | flow | 52.8MB/s | 55.6MB/s | 54.5MB/s |
Fix #329: complexity of has_sibling()
and has_child()
is now O(1), previously was linear (PR#330).
UNK
node) PR#234:
:foo: # parse error on the leading colon
:bar: a # parse error on the leading colon
:barbar: b # was ok
:barbarbar: c # was ok
foo: # was ok
bar: a # was ok
:barbar: b # was ok
:barbarbar: c # was ol
\r
to preserve roundtrip equivalence:
Tree tree;
NodeRef root = tree.rootref();
root |= MAP;
root["s"] = "t\rt";
root["s"] |= _WIP_VAL_DQUO;
std::string s = emitrs<std::string>(tree);
EXPECT_EQ(s, "s: \"t\\rt\"\n");
Tree tree2 = parse_in_arena(to_csubstr(s));
EXPECT_EQ(tree2["s"].val(), tree["s"].val());
seq:
- ""
- ''
- >
- | # error, the resulting val included all the YAML from the next node
seq2:
- ""
- ''
- |
- > # error, the resulting val included all the YAML from the next node
map:
a: ""
b: ''
c: >
d: | # error, the resulting val included all the YAML from the next node
map2:
a: ""
b: ''
c: |
d: > # error, the resulting val included all the YAML from the next node
lastly: the last
foo:
- bar
-
baz: qux
was wrongly parsed as
foo:
- bar
- baz: qux
\t
.@
or `
should be quoted.
0.1.2
.048
.std::vector<bool>
failed because its operator[]
returns a reference
instead of value_type
.Tree::_grow_arena()
, caused by using the arena position instead of its length as starting point for the new arena capacity.Tree::_clear_val()
: was clearing key instead (PR#335).{<<: *anchor, foo: bar}
are now correctly emitted as:
=VAL :<< # previously was =ALI <<
=ALI *anchor
=VAL :foo
=VAL :bar
#define
for the include guard of the amalgamated header.SOVERSION
missing from shared libraries.Published by github-actions[bot] over 2 years ago
Published by github-actions[bot] over 2 years ago
This release improves compliance with the YAML test suite (thanks @ingydotnet and @perlpunk for extensive and helpful cooperation), and adds node location tracking using the parser.
As part of the new feature to track source locations, opportunity was taken to address a number of pre-existing API issues. These changes consisted of:
c4::yml::parse()
and c4::yml::Parser::parse()
overloads; all these functions will be removed in short order. Until removal, any call from client code will trigger a compiler warning.parse()
alternatives, either parse_in_place()
or parse_in_arena()
:
parse_in_place()
receives only substr
buffers, ie mutable YAML source buffers. Trying to pass a csubstr
buffer to parse_in_place()
will cause a compile error:
substr readwrite = /*...*/;
Tree tree = parse_in_place(readwrite); // OK
csubstr readonly = /*...*/;
Tree tree = parse_in_place(readonly); // compile error
parse_in_arena()
receives only csubstr
buffers, ie immutable YAML source buffers. Prior to parsing, the buffer is copied to the tree's arena, then the copy is parsed in place. Because parse_in_arena()
is meant for immutable buffers, overloads receiving a substr
YAML buffer are now declared but marked deprecated, and intentionally left undefined, such that calling parse_in_arena()
with a substr
will cause a linker error as well as a compiler warning.
substr readwrite = /*...*/;
Tree tree = parse_in_arena(readwrite); // compile warning+linker error
This is to prevent an accidental extra copy of the mutable source buffer to the tree's arena: substr
is implicitly convertible to csubstr
. If you really intend to parse an originally mutable buffer in the tree's arena, convert it first explicitly to immutable by assigning the substr
to a csubstr
prior to calling parse_in_arena()
:
substr readwrite = /*...*/;
csubstr readonly = readwrite; // ok
Tree tree = parse_in_arena(readonly); // ok
This problem does not occur with parse_in_place()
because csubstr
is not implicitly convertible to substr
.ryml.parse()
was removed and not just deprecated; the parse_in_arena()
and parse_in_place()
now replace this.Callbacks
: changed behavior in Parser
and Tree
:
// To obtain locations, use of the parser is needed:
ryml::Parser parser;
ryml::Tree tree = parser.parse_in_arena("source.yml", R"({
aa: contents,
foo: [one, [two, three]]
})");
// After parsing, on the first call to obtain a location,
// the parser will cache a lookup structure to accelerate
// tracking the location of a node, with complexity
// O(numchars(srcbuffer)). Then it will do the lookup, with
// complexity O(log(numlines(srcbuffer))).
ryml::Location loc = parser.location(tree.rootref());
assert(parser.location_contents(loc).begins_with("{"));
// note the location members are zero-based:
assert(loc.offset == 0u);
assert(loc.line == 0u);
assert(loc.col == 0u);
// On the next call to location(), the accelerator is reused
// and only the lookup is done.
loc = parser.location(tree["aa"]);
assert(parser.location_contents(loc).begins_with("aa"));
assert(loc.offset == 2u);
assert(loc.line == 1u);
assert(loc.col == 0u);
// KEYSEQ in flow style: points at the key
loc = parser.location(tree["foo"]);
assert(parser.location_contents(loc).begins_with("foo"));
assert(loc.offset == 16u);
assert(loc.line == 2u);
assert(loc.col == 0u);
loc = parser.location(tree["foo"][0]);
assert(parser.location_contents(loc).begins_with("one"));
assert(loc.line == 2u);
assert(loc.col == 6u);
// SEQ in flow style: location points at the opening '[' (there's no key)
loc = parser.location(tree["foo"][1]);
assert(parser.location_contents(loc).begins_with("["));
assert(loc.line == 2u);
assert(loc.col == 11u);
loc = parser.location(tree["foo"][1][0]);
assert(parser.location_contents(loc).begins_with("two"));
assert(loc.line == 2u);
assert(loc.col == 12u);
loc = parser.location(tree["foo"][1][1]);
assert(parser.location_contents(loc).begins_with("three"));
assert(loc.line == 2u);
assert(loc.col == 17u);
// NOTE: reusing the parser with a new YAML source buffer
// will invalidate the accelerator.
See more details in the quickstart sample. Thanks to @cschreib for submitting a working example proving how simple it could be to achieve this.Parser
:
source()
and filename()
to get the latest buffer and filename to be parsedcallbacks()
to get the parser's callbacksfrom_tag_long()
and normalize_tag_long()
:
assert(from_tag_long(TAG_MAP) == "<tag:yaml.org,2002:map>");
assert(normalize_tag_long("!!map") == "<tag:yaml.org,2002:map>");
?
) (PR#212):
# all these were fixed:
? : # empty
? explicit key # this comment was not parsed correctly
? # trailing empty key was not added to the map
:
or -
. This feature is costly (see some benchmark results here) and thus it is disabled by default, and requires defining a macro or cmake option RYML_WITH_TAB_TOKENS
to enable (PR#211).{"like":"this"}
without any need for preprocessing. This code was not valid YAML 1.1, but was made valid in YAML 1.2. So the preprocess_json()
functions, used to insert spaces after :
are no longer necessary and have been removed. If you were using these functions, remove the calls and just pass the original source directly to ryml's parser (PR#210).---
|
hello
there
---
|
ciao
qua
---
- |
hello
there
- |
ciao
qua
---
foo: |
hello
there
bar: |
ciao
qua
foo0 : bar
---
foo1 : bar # the " :" was causing an assert
---
foo2 : bar
---
foo3 : bar
---
foo4 : bar
%
are emitted with quotes ((PR#216).null
or ~
to null scalar strings. Now the scalar strings contain the verbatim contents of the original scalar; to query whether a scalar value is null, use Tree::key_is_null()/val_is_null()
and NodeRef::key_is_null()/val_is_null()
which return true if it is empty or any of the unquoted strings ~
, null
, Null
, or NULL
. (PR#207):"\\\"\n\r\t\<TAB>\/\<SPC>\0\b\f\a\v\e\_\N\L\P"
(PR#207).\x
\u
\U
in double-quoted scalars:
Tree tree = parse_in_arena(R"(["\u263A \xE2\x98\xBA \u2705 \U0001D11E"])");
assert(tree[0].val() == "☺ ☺ ✅ 𝄞");
This is mandated by the YAML standard and was missing from ryml (PR#207).-
from the parent node was added (PR#210):
const ryml::Tree tree = ryml::parse_in_arena(R"(
- - Rochefort 10
- Busch
- Leffe Rituel
- - and so
- many other
- wonderful beers
)");
// before (error), YAML valid but not expected
//assert(ryml::emitrs<std::string>(tree[0][3]) == R"(- - and so
// - many other
// - wonderful beers
//)");
// now: YAML valid and expected
assert(ryml::emitrs<std::string>(tree[0][3]) == R"(- and so
- many other
- wonderful beers
)");
!
: should be an empty val tagged with !
(UKK06-02) (PR#215).#include <stdarg.h>
which prevented compilation in bare-metal arm-none-eabi
(PR #195, requiring also c4core #64).infinity
,inf
and nan
as special float values (but not mixed case: eg InFiNiTy
or Inf
or NaN
are not accepted) (PR #186)..Inf
, .INF
, .NaN
, .NAN
. Previously, only low-case .inf
and .nan
were accepted (PR #186).null
with upper or mixed case: Null
or NULL
. Previously, only low-case null
was accepted (PR #186).c4/substr_fwd.hpp
: (failure in Xcode 12 and earlier) forward declaration for std::allocator
is inside the inline namespace __1
, unlike later versions.c4/error.hpp
: (failure in debug mode in Xcode 11 and earlier) __clang_major__
does not mean the same as in the common clang, and as a result the warning -Wgnu-inline-cpp-without-extern
does not exist there.[[[
(PR#210). Same thing for []]
.Rewrite filtering of scalars to improve parsing performance (PR #188). Previously the scalar strings were filtered in place, which resulted in quadratic complexity in terms of scalar length. This did not matter for small scalars fitting the cache (which is the more frequent case), but grew in cost as the scalars grew larger. To achieve linearity, the code was changed so that the strings are now filtered to a temporary scratch space in the parser, and copied back to the output buffer after filtering, if any change occurred. The improvements were large for the folded scalars; the table below shows the benchmark results of throughput (MB/s) for several files containing large scalars of a single type:
scalar type | before | after | improvement |
---|---|---|---|
block folded | 276 | 561 | 103% |
block literal | 331 | 611 | 85% |
single quoted | 247 | 267 | 8% |
double quoted | 212 | 230 | 8% |
plain (unquoted) | 173 | 186 | 8% |
The cost for small scalars is negligible, with benchmark improvement in the interval of -2% to 5%, so well within the margin of benchmark variability in a regular OS. In the future, this will be optimized again by copying each character in place, thus completely avoiding the staging arena.
Callbacks
: add operator==()
and operator!=()
(PR #168).
Tree
: on error or assert prefer the error callback stored into the tree's current Callbacks
, rather than the global Callbacks
(PR #168).
detail::stack<>
: improve behavior when assigning from objects Callbacks
, test all rule-of-5 scenarios (PR #168).
Improve formatting of error messages.
Published by github-actions[bot] almost 3 years ago
Despite ryml being still in a non-stable 0.x.y version, considerable effort goes into trying to avoid breaking changes. However, this release has to collect on the semantic versioning prerogative for breaking changes. This is a needed improvement, so sorry for any nuisance!
The allocation and error callback logic was revamped on the amalgamation PR. Now trees and parsers receive (and store) a full ryml::Callbacks
object instead of the (now removed) ryml::Allocator
which had a pointer to a (now removed) ryml::MemoryResourceCallbacks
, which was a (now removed) ryml::MemoryResource
. To be clear, the Callbacks
class is unchanged, other than removing some unneeded helper methods.
These changes were motivated by unfortunate name clashes between c4::Allocator/ryml::Allocator
and c4::MemoryResource/ryml::MemoryResource
, occurring if <c4/allocator.hpp>
or <c4/memory_resource.hpp>
were included before <c4/yml/common.hpp>
. They also significantly simplify this part of the API, making it really easier to understand.
As a consequence of the above changes, the global memory resource getters and setters for ryml were also removed: ryml::get_memory_resource()/ryml::set_memory_resource()
.
Here's an example of the required changes in client code. First the old client code (from the quickstart):
struct PerTreeMemoryExample : public ryml::MemoryResource
{
void *allocate(size_t len, void * hint) override;
void free(void *mem, size_t len) override;
};
PerTreeMemoryExample mrp;
PerTreeMemoryExample mr1;
PerTreeMemoryExample mr2;
ryml::Parser parser = {ryml::Allocator(&mrp)};
ryml::Tree tree1 = {ryml::Allocator(&mr1)};
ryml::Tree tree2 = {ryml::Allocator(&mr2)};
Should now be rewritten to:
struct PerTreeMemoryExample
{
ryml::Callbacks callbacks() const; // helper to create the callbacks
};
PerTreeMemoryExample mrp;
PerTreeMemoryExample mr1;
PerTreeMemoryExample mr2;
ryml::Parser parser = {mrp.callbacks()};
ryml::Tree tree1 = {mr1.callbacks()};
ryml::Tree tree2 = {mr2.callbacks()};
$ python tools/amalgamate.py ryml_all.hpp
#define RYML_SINGLE_HDR_DEFINE_NOW
and then #include <ryml_all.hpp>
. This will enable the function and class definitions in the header file. For example, here's a sample program:
#include <iostream>
#define RYML_SINGLE_HDR_DEFINE_NOW // do this before the include
#include <ryml_all.hpp>
int main()
{
auto tree = ryml::parse("{foo: bar}");
std::cout << tree["foo"].val() << "\n";
}
Tree::change_type()
and NodeRef::change_type()
(PR #171):
// clears a node and sets its type to a different type (one of `VAL`, `SEQ`, `MAP`):
Tree t = parse("{keyval0: val0, keyval1: val1, keyval2: val2}");
t[0].change_type(VAL);
t[1].change_type(MAP);
t[2].change_type(SEQ);
Tree expected = parse("{keyval0: val0, keyval1: {}, keyval2: []}");
assert(emitrs<std::string>(t) == emitrs<std::string>(expected));
foo:
- |
child0
- |2
child2 # indentation is 4, not 2
# previously this resulted in a parse error
- - : empty key
- - : another empty key
substr
and csubstr
by value instead of const reference (PR #171)ryml::ryml
(PR #174)export()
(PR #179).Tree
(PR #181).Published by github-actions[bot] almost 3 years ago
This release is focused on bug fixes and compliance with the YAML test suite.
Tree
and NodeRef
: add document getter doc()
and docref()
Tree tree = parse(R"(---
doc0
---
doc1
)");
NodeRef stream = t.rootref();
assert(stream.is_stream());
// tree.doc(i): get the index of the i-th doc node.
// Equivalent to tree.child(tree.root_id(), i)
assert(tree.doc(0) == 1u);
assert(tree.doc(1) == 2u);
// tree.docref(i), same as above, return NodeRef
assert(tree.docref(0).val() == "doc0");
assert(tree.docref(1).val() == "doc1");
// stream.doc(i), same as above, given NodeRef
assert(stream.doc(0).val() == "doc0");
assert(stream.doc(1).val() == "doc1");
C4CORE_NO_FAST_FLOAT
(PR #163)# test case UT92
# all parsed as "matches %": 20
- { matches
% : 20 }
- { matches
%: 20 }
- { matches
%:
20 }
# test case 735Y
- !!map # Block collection
foo : bar
---
# test case 5GBF
"Empty line
<TAB>
as a line feed"
# now correctly parsed as "Empty line\nas a line feed"
---
# test case PRH3
' 1st non-empty
<SPC>2nd non-empty<SPC>
<TAB>3rd non-empty '
# now correctly parsed as " 1st non-empty\n2nd non-empty 3rd non-empty "
# test cases NP9H, Q8AD
"folded<SPC>
to a space,<TAB>
<SPC>
to a line feed, or <TAB>\
\ <TAB>non-content"
# now correctly parsed as "folded to a space,\nto a line feed, or \t \tnon-content"
# all scalars now correctly parsed as "quoted string",
# both for double and single quotes
---
"quoted
string"
--- "quoted
string"
---
- "quoted
string"
---
- "quoted
string"
---
"quoted
string": "quoted
string"
---
"quoted
string": "quoted
string"
# the block scalar specs below now have the same effect.
# test cases: D83L, P2AD
- |2-
explicit indent and chomp
- |-2
chomp and explicit indent
# test cases: 4QFQ, 7T8X
- >
# child1
# parsed as "\n\n child1"
--- # test case DWX9
|
literal
text
# Comment
# parsed as "\n\nliteral\n \n\ntext\n"
# test case W4TN
# all docs have the same value: "%!PS-Adobe-2.0"
--- |
%!PS-Adobe-2.0
...
--- >
%!PS-Adobe-2.0
...
--- |
%!PS-Adobe-2.0
...
--- >
%!PS-Adobe-2.0
...
--- |
%!PS-Adobe-2.0
--- >
%!PS-Adobe-2.0
--- |
%!PS-Adobe-2.0
--- >
%!PS-Adobe-2.0
# test case 6VJK
# now correctly parsed as "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n"
>
Sammy Sosa completed another
fine season with great stats.
63 Home Runs
0.288 Batting Average
What a year!
---
# test case MJS9
# now correctly parsed as "foo \n\n \t bar\n\nbaz\n"
>
foo<SPC>
<SPC>
<SPC><TAB><SPC>bar
baz
# test case F6MC
a: >2
more indented
regular
# parsed as a: " more indented\nregular\n"
b: >2
more indented
regular
# parsed as b: "\n\n more indented\nregular\n"
---
# test case NB6Z
key:
value
with
tabs
tabs
foo
bar
baz
# is now correctly parsed as "value with\ntabs tabs\nfoo\nbar baz"
---
# test case 9YRD, EX5H (trailing whitespace)
a
b
c
d
e
# is now correctly parsed as "a b c d\ne"
--- # this parsed
Bare
scalar
is indented
# was correctly parsed as "Bare scalar is indented"
--- # but this failed to parse successfully:
Bare
scalar
is not indented
# is now correctly parsed as "Bare scalar is not indented"
--- # test case NB6Z
value
with
tabs
tabs
foo
bar
baz
# now correctly parsed as "value with\ntabs tabs\nfoo\nbar baz"
---
--- # test cases EXG3, 82AN
---word1
word2
# now correctly parsed as "---word1 word2"
# test case 7TMG
--- # now correctly parsed as "word1"
word1
# comment
--- # now correctly parsed as [word1, word2]
[ word1
# comment
, word2]
is_anchor_or_ref()
is_key_quoted()
is_val_quoted()
is_quoted()
--- @mbs-c
--- @simu
--- @QuellaZhang
Published by github-actions[bot] about 3 years ago
Yank python package 0.2.1, was accidentally created while iterating the PyPI submission from the Github action. This release does not add any change, and is functionally the same as 0.2.1.
Published by github-actions[bot] about 3 years ago
This release is focused on bug fixes and compliance with the YAML test suite.
---
this is a scalar
--- # previously this was parsed as
- this is a scalar
Tree
and NodeRef
forward to the corresponding predicate in NodeType
NodeData
; use the equivalent call from Tree
or NodeRef
. For example, for is_map()
:
Tree t = parse("{foo: bar}");
size_t map_id = t.root_id();
NodeRef map = t.rootref();
t.get(map_id)->is_map(); // compile error: no longer exists
assert(t.is_map(map_id)); // OK
assert(map.is_map()); // OK
.has_*()
vs corresponding .is_*()
naming scheme.Tree::lookup_path_or_modify()
: add overload to graft existing branches (PR #141){&a a: &b b, *b: *a}
- &a # test case PW8X
- a
- &a : a
b: &b
- &c : &a
- ? &d
- ? &e
: &a
*
or &
or <<
are now correctly quoted when emitting (PR #156).<<: *anchor
or <<: [*anchor1, *anchor2]
now have a KEYREF
flag in their type (until a call to Tree::resolve()
):
Tree tree = parse("{map: &anchor {foo: bar}, copy: {<<: *anchor}}");
assert(tree["copy"]["<<"].is_key_ref()); // previously this did not hold
assert(tree["copy"]["<<"].is_val_ref()); // ... but this did
--- !!map {
k: !!seq [ a, !!str b],
j: !!seq
[ a, !!str b]
--- !!seq [
!!map { !!str k: v},
!!map { !!str ? k: v}
]
--- !!map
!!str foo: !!map # there was a parse error with the multiple tags
!!int 1: !!float 20.0
!!int 3: !!float 40.0
--- !!seq
- !!map
!!str k1: v1
!!str k2: v2
!!str k3: v3
"This has a\ttab"
# is now correctly parsed as "This has a<TAB>tab"
# test case 4ZYM, 7A4E, TL85
"
<SPC><SPC>foo<SPC>
<SPC>
<SPC><TAB><SPC>bar
<SPC><SPC>baz
"
# is now correctly parsed as " foo\nbar\nbaz "
---<TAB>scalar # test case K54U
---<TAB>{} # test case Q5MG
--- # test case DC7X
a: b<TAB>
seq:<TAB>
- a<TAB>
c: d<TAB>#X
# test case 4ABK
- {foo: , bar: , baz: } # this was parsed correctly as {foo: ~, bar: ~, baz: ~}
- {foo:, bar:, baz:} # ... but this was parsed as {'foo:': , 'bar:': ~, 'baz:': ~}
--- escaped slash: "a\/b" # test case 3UYS
# is now parsed as:
--- escaped slash: "a/b"
# test case 7T8X
- >
folded
line
next
line
* bullet
* list
* lines
last
line
is now correctly parsed as \nfolded line\nnext line\n * bullet\n\n * list\n * lines\n\nlast line\n
.# test case 3MYT
k:#foo
&a !t s
!t s
# now correctly parsed as "k:#foo &a !t s !t s"
# test case X8DW
? key
# comment
: value
# now correctly parsed as {key: value}
# test case 7W2P, ZWK4
? a
? b
c:
? d
e:
# now correctly parsed as {a: ~, b: ~, c: ~, d: ~, e: ~}
exec:
command:
# before the fix, this folded scalar failed to parse
- |
exec pg_isready -U "dog" -d "dbname=dog" -h 127.0.0.1 -p 5432
parses: no
Tree tree = parse("'this is a quoted scalar'");
assert(tree.rootref().is_doc());
assert(tree.rootref().is_val());
assert(tree.rootref().is_val_quoted());
--- # test cases 6XDY, 6ZKB, 9BXL, PUW8
---
---
is now parsed as
--- ~
--- ~
--- ~
!foo "bar"
...
# Global
%TAG ! tag:example.com,2000:app/
---
!foo "bar"
was parsed as
---
!foo "bar"
---
# notice the empty doc in here
---
!foo "bar"
and it is now correctly parsed as
---
!foo "bar"
---
!foo "bar"
(other than the known limitation that ryml does not do tag lookup)..nan
, .inf
, -.inf
(PR #149)preprocess_json()
: ensure quoted ranges are skipped when slurping containersPublished by github-actions[bot] over 3 years ago
as_json()
can now be called with tree and node id (4c23041)Parser::reserve_stack()
(f31fb9f)SO_VERSION
to shared buildssscanf(%f)
Published by github-actions[bot] almost 4 years ago
This is the first ryml release. Future releases will have a more organized changelog; for now, only recent major changes are listed.
Please be aware that there are still some anticipated breaking changes in the API before releasing the 1.0 major version. These are highlighted in the repo ROADMAP.
true
and false
are now parsed correctly into bool
variables:
auto tree = parse("{foo: true, bar: false}");
Emitting bool
variables still defaults to 0
/1
, like the default behaviour in the STL. To explicitly request true
/false
use c4::fmt::boolalpha()
:
node << var; // "1" or "0"
node << c4::fmt::boolalpha(var); // "true" or "false"
auto tree = parse("{foo: , bar: ''}");
// previous:
assert(tree["foo"].val() == "~");
assert(tree["bar"].val() == "");
// now:
assert(tree["foo"].val() == nullptr); // notice that this is now null
assert(tree["bar"].val() == "");
{foo: !!str, bar: ''} # now the comma does not cause an error
// previous
using pfn_error = void (*)(const char* msg, size_t msg_len, void *user_data);
// now:
using pfn_error = void (*)(const char* msg, size_t msg_len, Location location, void *user_data);