Skip to content

Commit

Permalink
Address TODOs about the need to document an unexpected aspect of how …
Browse files Browse the repository at this point in the history
…out-param

attributes are assigned.
  • Loading branch information
tzlaine committed Dec 16, 2023
1 parent 05bfffa commit 1bbad0a
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 15 deletions.
50 changes: 50 additions & 0 deletions doc/tutorial.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,56 @@ parse using a _ui_, and writing its attribute into a `double`. In general,
you can swap any type `T` out of the attribute, as long as `T` is not part of
the element type for some container within the attribute. ]

[heading An Odd Case]

When parsing using parser `P` into an attribute out-param `O`, you can pass
many types of things for `O` that are not `_ATTR_np_(P)`, as shown above.
This works because each step of attribute generation attemps to assign/append
to the associated out-param attribute, if there is one, and tries several
strategies to get that assignment/append operation to work.

In the previous section on attribute generation, I also described how
sequential parses of sequences will combine. For instance, consider: `P1 >>
P2 >> P3`. If the types `_ATTR_np_(P1)`, `_ATTR_np_(P2)`, and `_ATTR_np_(P3)`
are the same, and if that type is a container, `_ATTR_np_(P1 >> P2 >> P3)` is
`_ATTR_np_(P1)`.

However, since you can pass just about any old thing as `O`, and since there
are three assign/append steps (one for each parser in the sequence) involved
in parsing `P1 >> P2 >> P3`, you can get unexpected results if you pass a
scalar value for `O`. For example:

using namespace boost::parser;
constexpr auto parser = string("a") >> string("b") >> string("c") |
string("x") >> string("y") >> string("z");
std::string str = "abc";
std::any chars;
assert(parse(str, parser, chars));
assert(std::any_cast<std::string>(chars) == "c");

Note that `chars` contains the last string `"c"`, and not the accumulation of
all three, `"abc"`. This happens because each step of the first alternative
`string("a") >> string("b") >> string("c")` succeeds, and after each
subparser, the result of that parse is assigned/appended to the attribute
out-param. Since appending would not work (you cannot append to a sequence
inside a `std::any`, because you cannot see that there is a sequence in
there), assignment is done instead. This assignemnt is done for each of the
three subparsers, but of course it replaces what in the `std::any` previously.

If we had passed a `std::string` for `O` instead, the result would hae been
much less surprising.

using namespace boost::parser;
constexpr auto parser = string("a") >> string("b") >> string("c") |
string("x") >> string("y") >> string("z");
char const * str = "xyz";
std::string chars;
assert(parse(str, parser, chars));
assert(chars, "xyz");

The result has the accumulation of `"abc"` because the out-param type
`std::string` is a container.

[heading Unicode versus non-Unicode parsing]

A call to _p_ either considers the entire input to be in a UTF format (UTF-8,
Expand Down
7 changes: 0 additions & 7 deletions include/boost/parser/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3232,13 +3232,6 @@ namespace boost { namespace parser {
constexpr bool attr_container = container<attr_t>;

if constexpr (out_container == attr_container) {
// TODO: Document that the user can pass anything she
// likes for retval, but that if retval is not a
// container, but the default retval would have been,
// weird assignments may occur. For instance,
// parse(first, last, char_ >> char_, a), where 'a' is
// a boost::any, will only yeild a single char in the
// any.
parser.call(
use_cbs,
first,
Expand Down
8 changes: 4 additions & 4 deletions test/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#include <gtest/gtest.h>

#include <any>


using namespace boost::parser;

Expand Down Expand Up @@ -1455,14 +1457,12 @@ TEST(parser, combined_seq_and_or)
{
constexpr auto parser = string("a") >> string("b") >> string("c") |
string("x") >> string("y") >> string("z");
#if 0 // TODO: Document why this assigns "c" instead of "abc" to the any.
{
std::string str = "abc";
boost::parser::detail::any_copyable chars;
std::any chars;
EXPECT_TRUE(parse(str, parser, chars));
EXPECT_EQ(chars.cast<std::string>(), "abc");
EXPECT_EQ(std::any_cast<std::string>(chars), "c");
}
#endif

{
std::string str = "xyz";
Expand Down
7 changes: 3 additions & 4 deletions test/parser_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <gtest/gtest.h>

#include <any>
#include <deque>


Expand Down Expand Up @@ -1722,14 +1723,12 @@ TEST(parser, combined_seq_and_or)
{
constexpr auto parser = string("a") >> string("b") >> string("c") |
string("x") >> string("y") >> string("z");
#if 0 // TODO: Document why this assigns "c" instead of "abc" to the any.
{
char const * str = "abc";
boost::parser::detail::any_copyable chars;
std::any chars;
EXPECT_TRUE(parse(str, parser, chars));
EXPECT_EQ(chars.cast<std::string>(), "abc");
EXPECT_EQ(std::any_cast<std::string>(chars), "c");
}
#endif

{
char const * str = "xyz";
Expand Down

0 comments on commit 1bbad0a

Please sign in to comment.