|
rapidyaml
0.9.0
parse and emit YAML, and do it fast
|
rapidyaml implements its parsing logic with a two-level model, where a ParseEngine object reads through the YAML source, and dispatches events to an EventHandler bound to the ParseEngine. More...
Classes | |
| struct | c4::yml::EventHandlerStack< HandlerImpl, HandlerState > |
| Use this class a base of implementations of event handler to simplify the stack logic. More... | |
| struct | c4::yml::EventHandlerTreeState |
| The stack state needed specifically by EventHandlerTree. More... | |
| struct | c4::yml::EventHandlerTree |
| The event handler to create a ryml Tree. More... | |
| struct | c4::yml::EventHandlerYamlStdState |
| The stack state needed specifically by EventHandlerYamlStd. More... | |
| struct | c4::yml::EventHandlerYamlStd |
| The event handler producing standard YAML events as used in the YAML test suite. More... | |
Functions | |
| void | c4::yml::append_escaped (extra::string *s, csubstr val) |
rapidyaml implements its parsing logic with a two-level model, where a ParseEngine object reads through the YAML source, and dispatches events to an EventHandler bound to the ParseEngine.
Because ParseEngine is templated on the event handler, the binding uses static polymorphism, without any virtual functions. The actual handler object can be changed at run time, (but of course needs to be the type of the template parameter). This is thus a very efficient architecture, and further enables the user to provide his own custom handler if he wishes to bypass the rapidyaml Tree.
There are two handlers implemented in this project:
The event model used by the parse engine and event handlers follows very closely the event model in the YAML test suite.
Consider for example this YAML,
which would produce these events in the test-suite parlance:
For reference, the ParseEngine object will produce this sequence of calls to its bound EventHandler:
For many other examples of all areas of YAML and how ryml's parse model corresponds to the YAML standard model, refer to the [unit tests for the parse engine](https://github.com/biojppm/rapidyaml/tree/master/test/test_parse_engine.cpp).
Most of the parsing events adopted by rapidyaml in its event model are fairly obvious, but there are two less-obvious events requiring some explanation.
These events exist to make it easier to parse some special YAML cases. They are called by the parser when a just-handled value/container is actually the first key of a new map:
actually_val_is_first_key_of_new_map_flow() (see implementation in EventHandlerTree / see implementation in EventHandlerYamlStd)actually_val_is_first_key_of_new_map_block() (see implementation in EventHandlerTree / see implementation in EventHandlerYamlStd)For example, consider an implicit map inside a seq: [a: b, c: d] which is parsed as [{a: b}, {c: d}]. The standard event sequence for this YAML would be the following:
The problem with this event sequence is that it forces the parser to delay setting the val scalar (in this case "a" and "c") until it knows whether the scalar is a key or a val. This would require the parser to store the scalar until this time. For instance, in the example above, the parser should delay setting "a" and "c", because they are in fact keys and not vals. Until then, the parser would have to store "a" and "c" in its internal state. The downside is that this complexity cost would apply even if there is no implicit map – every val in a seq would have to be delayed until one of the disambiguating subsequent tokens ,-]: is found. By calling this function, the parser can avoid this complexity, by preemptively setting the scalar as a val. Then a call to this function will create the map and rearrange the scalar as key. Now the cost applies only once: when a seqimap starts. So the following (easier and cheaper) event sequence below has the same effect as the event sequence above:
This also applies to container keys (although ryml's tree cannot accomodate these): the parser can preemptively set a container as a val, and call this event to turn that container into a key. For example, consider this yaml:
The standard event sequence for this YAML would be the following:
The problem with the sequence above is that, reading from left-to-right, the parser can only detect the proper calls at (1) and (2) once it reaches (1) in the YAML source. So, the parser would have to buffer the entire event sequence starting from the beginning until it reaches (1). Using this function, the parser can do instead:
| void c4::yml::append_escaped | ( | extra::string * | s, |
| csubstr | val | ||
| ) |
Definition at line 15 of file test_suite_event_handler.cpp.
References _c4flush_use_instead.