Skip to content

Commit

Permalink
Merge branch 'master' into gh-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
tzlaine committed Jan 14, 2024
2 parents 6ec9ca0 + d17268d commit 7bf7ea1
Show file tree
Hide file tree
Showing 258 changed files with 1,532 additions and 538,292 deletions.
21 changes: 6 additions & 15 deletions doc/intro.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -100,31 +100,22 @@ significantly increase compile times. Also, MSVC seems to have a hard time
with large values; I successfully set this value to `50` on MSVC, but `100`
broke the MSVC build entirely.

_Parser_ uses `std::optional` and `std::variant` internally. There is no way
to change this. However, when _Parser_ generates values as a result of the
parse (see _attr_gen_), it can place them into other implementations of
optional and/or variant, if you tell it to do so. You tell it which templates
are usable as an optional or variant by specializing the associated variable
template. For instance, here is how you would tell _Parser_ that
`boost::optional` is an optional-type:
_Parser_ uses `std::optional` internally. There is no way to change this.
However, when _Parser_ generates values as a result of the parse (see
_attr_gen_), it can place them into other implementations of optional, if you
tell it to do so. You tell it a template is usable as an optional by
specializing the `enable_optional` template. For instance, here is how you
would tell _Parser_ that `boost::optional` is an optional-type:

template<typename T>
constexpr bool boost::parser::enable_optional<boost::optional<T>> = true;

Here's how you would do the same thing for `boost::variant2::variant`:

template<typename... Ts>
constexpr bool boost::parser::enable_optional<boost::variant2::variant<Ts...>> = true;

The requirements on a template used as an optional are pretty simple, since
_Parser_ does almost nothing but assign to them. For a type `O` to be a
usable optional, you must be able to assign to `O`, and `O` must have an
`operator*` that returns the stored value, or a (possibly cv-qualified)
reference to the stored value.

For variants, the requirement is even simpler; the variant type only needs to
be assignable.

[endsect]

[section This Library's Relationship to Boost.Spirit]
Expand Down
5 changes: 5 additions & 0 deletions doc/parser.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,17 @@
[def _no_case_ [globalref boost::parser::no_case `no_case[]`]]
[def _string_view_ [globalref boost::parser::string_view `string_view[]`]]
[def _skip_ [globalref boost::parser::skip `skip[]`]]
[def _merge_ [globalref boost::parser::merge `merge[]`]]
[def _sep_ [globalref boost::parser::separate `separate[]`]]

[def _omit_np_ [globalref boost::parser::omit `omit`]]
[def _raw_np_ [globalref boost::parser::raw `raw`]]
[def _lexeme_np_ [globalref boost::parser::lexeme `lexeme`]]
[def _no_case_np_ [globalref boost::parser::no_case `no_case`]]
[def _string_view_np_ [globalref boost::parser::string_view `string_view`]]
[def _skip_np_ [globalref boost::parser::skip `skip`]]
[def _merge_np_ [globalref boost::parser::merge `merge`]]
[def _sep_np_ [globalref boost::parser::separate `separate`]]

[def _alnum_ [globalref boost::parser::ascii::alnum `ascii::alnum`]]
[def _alpha_ [globalref boost::parser::ascii::alpha `ascii::alpha`]]
Expand All @@ -188,6 +192,7 @@

[def _p_api_ [link boost_parser__proposed_.tutorial.the__parse____api the `parse()` API]]
[def _rule_parsers_ [link boost_parser__proposed_.tutorial.rule_parsers Rule Parsers]]
[def _parsing_structs_ [link boost_parser__proposed_.tutorial.parsing__struct_s Parsing `struct`s]]
[def _expect_pts_ [link boost_parser__proposed_.tutorial.backtracking.html#boost_parser__proposed_.tutorial.backtracking.expectation_points Expectation points]]
[def _attr_gen_ [link boost_parser__proposed_.tutorial.attribute_generation Attribute Generation]]
[def _directives_ [link boost_parser__proposed_.tutorial.directives Directives]]
Expand Down
32 changes: 32 additions & 0 deletions doc/rationale.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,36 @@ are instead arbitrary types being constructed and inserted into containers.
Allowing the insertion to happen on arbitrary types that model the `container`
concept is what allows generic use of different containers.

[heading Sequences of character type are treated specially]

_Parser_ attempts to keep the rules for attribute generation simple. However,
there are some rules for attribute generation that only apply to character
types like `char` and `char32_t`. Sequences of these produce a `std::string`
attribute, while sequences of every other type produce `std::vector`s. There
are a couple of reasons for this.

First, strings and vectors are different. We know that strings are just
arrays of numbers, but we have a whole different type for them, `std::string`.
It has a different API, and other code that operates on text expects a string
instead of some other container. Arrays of characters are already considered
special by the standard library and common practice in C++.

Second, When you write a parser that parses multiple characters in a row, you
are typically trying to produce a string attribute, rather than a few
individual character values. When you use multiple non-character parsers in a
row, you are typically trying to produce multiple values. For instance:

namespace bp = boost::parser;
auto parser_1 = bp::char_ >> bp::char_ >> -bp::char_;
auto parser_2 = +(bp::char_ - ' ') >> ' ' >> +(bp::char_ - ' ');

I don't know about you, but I've rarely written a parser like `parser_1` and
wanted to produce a `_bp_tup_<char, char, std::optional<char>>`. Similarly,
I've rarely written a parser like `parser_2` and wanted a
`std::vector<std::string>`.

_Parser_ therefore makes the common case the default behavior, and provides
you with the _merge_ and _sep_ directives to let you opt-in to generating the
less-common attributes.

[endsect]
84 changes: 83 additions & 1 deletion doc/tutorial.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -1912,7 +1912,89 @@ same attribute generation rules.
[[`p1 | p2[a] | p3`] [`std::optional<std::variant<_ATTR_np_(p1), _ATTR_np_(p3)>>`]]
]

[heading Directives that affect attribute generation]
[heading Controlling attribute generation with _merge_ and _sep_]

As we saw in the previous _parsing_structs_ section, if you parse two strings
in a row, you get two separate strings in the resulting attribute. The parser
from that example was this:

namespace bp = boost::parser;
auto employee_parser = bp::lit("employee")
>> '{'
>> bp::int_ >> ','
>> quoted_string >> ','
>> quoted_string >> ','
>> bp::double_
>> '}';

`employee_parser`'s attribute is `_bp_tup_<int, std::string, std::string,
double>`. The two `quoted_string` parsers produce `std::string` attributes,
and those attributes are not combined. That is the default behavior, and it
is just what we want for this case; we don't want the first and last name
fields to be jammed together such that we can't tell where one name ends and
the other begins. What if we were parsing some string that consisted of a
prefix and a suffix, and the prefix and suffix were defined separately for
reuse elsewhere?

namespace bp = boost::parser;
auto prefix = /* ... */;
auto suffix = /* ... */;
auto special_string = prefix >> suffix;
// Continue to use prefix and suffix to make other parsers....

In this case, we might want to use these separate parsers, but want
`special_string` to produce a single `std::string` for its attribute. _merge_
exists for this purpose.

namespace bp = boost::parser;
auto prefix = /* ... */;
auto suffix = /* ... */;
auto special_string = bp::merge[prefix >> suffix];

_merge_ only applies to sequence parsers (like `p1 >> p2`), and forces all
subparsers in the sequence parser to use the same variable for their
attribute.

Another directive, _sep_, also applies only to sequence parsers, but does the
opposite of _merge_. If forces all the attributes produced by the subparsers
of the sequence parser to stay separate, even if they would have combined.
For instance, consider this parser.

namespace bp = boost::parser;
auto string_and_char = +bp::char_('a') >> ' ' >> bp::cp;

`string_and_char` matches one or more `'a'`s, followed by some other
character. As written above, `string_and_char` produces a `std::string`, and
the final character is appended to the string, after all the `'a'`s. However,
if you wanted to store the final character as a separate value, you would use
_sep_.

namespace bp = boost::parser;
auto string_and_char = bp::separate[+bp::char_('a') >> ' ' >> bp::cp];

With this change, `string_and_char` produces the attribute
`_bp_tup_<std::string, char32_t>`.

[heading _merge_ and _sep_ in more detail]

As mentioned previously, _merge_ applies only to sequence parsers. All
subparsers must have the same attribute, or produce no attribute at all. At
least one subparser must produce an attribute. When you use _merge_, you
create a /combining group/. Every parser in a combining group uses the same
variable for its attribute. No parser in a combining group interacts with the
attributes of any parsers outside of its combining group. Combining groups
are disjoint; `merge[/*...*/] >> merge[/*...*/]` will produce a tuple of two
attributes, not one.

_sep_ also applies only to sequence parsers. When you use _sep_, you disable
interaction of all the subparsers' attributes with adjacent attributes,
whether they are inside or outside the _sep_ directive; you force each
subparser to have a separate attribute.

The rules for _merge_ and _sep_ overrule the steps of the algorithm described
above for combining the attributes of a sequence parser.

[heading Other directives that affect attribute generation]

`_omit_np_[p]` disables attribute generation for the parser `p`.
`_raw_np_[p]` changes the attribute from `_ATTR_np_(p)` to a view that
Expand Down
74 changes: 32 additions & 42 deletions include/boost/parser/detail/printing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,15 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);

template<typename Context, typename ParserTuple, typename BacktrackingTuple>
template<
typename Context,
typename ParserTuple,
typename BacktrackingTuple,
typename CombiningGroups>
void print_parser(
Context const & context,
seq_parser<ParserTuple, BacktrackingTuple> const & parser,
seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups> const &
parser,
std::ostream & os,
int components = 0);

Expand Down Expand Up @@ -460,51 +465,36 @@ namespace boost { namespace parser { namespace detail {
}
};

template<typename... T>
inline void print(std::ostream & os, tuple<T...> const & attr);

template<typename... T>
inline void print(std::ostream & os, std::variant<T...> const & attr);

template<typename T>
inline void print(std::ostream & os, std::optional<T> const & attr);

template<typename Attribute>
inline void print(std::ostream & os, Attribute const & attr);

template<typename... T>
inline void print(std::ostream & os, tuple<T...> const & attr)
{
os << "(";
bool first = false;
hl::for_each(attr, [&](auto const & a) {
if (first)
os << ", ";
detail::print(os, a);
first = false;
});
os << ")\n";
}

template<typename... T>
inline void print(std::ostream & os, std::variant<T...> const & attr)
{
os << "<<variant>>";
}

template<typename T>
inline void print(std::ostream & os, std::optional<T> const & attr)
{
if (!attr)
os << "<<empty>>";
else
detail::print(os, *attr);
}
constexpr bool is_variant_v = false;
template<typename... Ts>
constexpr bool is_variant_v<std::variant<Ts...>> = true;

template<typename Attribute>
inline void print(std::ostream & os, Attribute const & attr)
{
printer<Attribute>{}(os, attr);
using just_attribute =
std::remove_cv_t<std::remove_reference_t<Attribute>>;
if constexpr (is_tuple<just_attribute>{}) {
os << "(";
bool first = false;
hl::for_each(attr, [&](auto const & a) {
if (first)
os << ", ";
detail::print(os, a);
first = false;
});
os << ")\n";
} else if constexpr (is_optional_v<just_attribute>) {
if (!attr)
os << "<<empty>>";
else
detail::print(os, *attr);
} else if constexpr (is_variant_v<just_attribute>) {
os << "<<variant>>";
} else {
printer<just_attribute>{}(os, attr);
}
}

template<typename Attribute>
Expand Down
42 changes: 34 additions & 8 deletions include/boost/parser/detail/printing_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,12 @@ namespace boost { namespace parser { namespace detail {
struct n_aray_parser<or_parser<ParserTuple>> : std::true_type
{};

template<typename ParserTuple, typename BacktrackingTuple>
struct n_aray_parser<seq_parser<ParserTuple, BacktrackingTuple>>
template<
typename ParserTuple,
typename BacktrackingTuple,
typename CombiningGroups>
struct n_aray_parser<
seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups>>
: std::true_type
{};

Expand Down Expand Up @@ -181,34 +185,51 @@ namespace boost { namespace parser { namespace detail {
});
}

template<typename Context, typename ParserTuple, typename BacktrackingTuple>
template<
typename Context,
typename ParserTuple,
typename BacktrackingTuple,
typename CombiningGroups>
void print_parser(
Context const & context,
seq_parser<ParserTuple, BacktrackingTuple> const & parser,
seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups> const &
parser,
std::ostream & os,
int components)
{
int prev_group = 0;
int i = 0;
bool printed_ellipsis = false;
using combining_groups =
detail::combining_t<ParserTuple, CombiningGroups>;
hl::for_each(
hl::zip(parser.parsers_, BacktrackingTuple{}),
hl::zip(parser.parsers_, BacktrackingTuple{}, combining_groups{}),
[&](auto const & parser_and_backtrack) {
using namespace literals;
auto const & parser = parser::get(parser_and_backtrack, 0_c);
auto const backtrack = parser::get(parser_and_backtrack, 1_c);
auto const group = parser::get(parser_and_backtrack, 2_c);

if (components == parser_component_limit) {
if (!printed_ellipsis)
if (!printed_ellipsis) {
os << (backtrack ? " >> ..." : " > ...");
}
printed_ellipsis = true;
return;
}
if (group != prev_group && prev_group)
os << ']';
if (i)
os << (backtrack ? " >> " : " > ");
if (group != prev_group && group)
os << (group == -1 ? "separate[" : "merge[");
detail::print_parser(context, parser, os, components);
++components;
++i;
prev_group = group;
});
if (prev_group && !printed_ellipsis)
os << ']';
}

template<typename Context, typename Parser, typename Action>
Expand Down Expand Up @@ -786,10 +807,15 @@ namespace boost { namespace parser { namespace detail {
os << "double_";
}

template<typename Context, typename ParserTuple, typename BacktrackingTuple>
template<
typename Context,
typename ParserTuple,
typename BacktrackingTuple,
typename CombiningGroups>
void print_switch_matchers(
Context const & context,
seq_parser<ParserTuple, BacktrackingTuple> const & parser,
seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups> const &
parser,
std::ostream & os,
int components)
{
Expand Down
Loading

0 comments on commit 7bf7ea1

Please sign in to comment.