2
0
mirror of https://github.com/boostorg/parser.git synced 2026-01-19 04:22:13 +00:00

Be more explicit in the reference nad tutorial descriptions of

BOOST_PARSER_DEFINE_RULES about what exactly it does and how to use it.

Fixes #101.
This commit is contained in:
Zach Laine
2024-02-23 21:38:15 -06:00
parent 2697eebffd
commit eda6238180
3 changed files with 20 additions and 64 deletions

View File

@@ -185,9 +185,6 @@ that have been retained in _Parser_. Both libraries:
* use approximately the same set of directives to influence the parse
(e.g. `lexeme[]`);
* provide loosely-coupled rules that are separately compilable (at least for
Spirit X3); and
* are built around a flexible parse context object that has state added to and
removed from it during the parse (again, comparing to Spirit X3).

View File

@@ -558,13 +558,16 @@ parser `doubles_def` work together. The `_def` suffix is a naming convention
that this macro relies on to work. The tag type allows the rule parser,
`doubles`, to call one of these overloads when used as a parser.
[note In case you're curious about exactly what code is produced by the above
use of _RULES_, it's two overloads of a function called `parse_rule()`. The
overloads each take a `struct doubles` parameter (to distinguish them from the
other overloads of `parse_rule()` for other rules) and parse using
`doubles_def`. You will never need to call any overload of `parse_rule()`
yourself; it is used internally by the parser that implements _rs_,
`rule_parser`.]
_RULES_ expands to two overloads of a function called `parse_rule()`. In the
case above, the overloads each take a `struct doubles` parameter (to
distinguish them from the other overloads of `parse_rule()` for other rules)
and parse using `doubles_def`. You will never need to call any overload of
`parse_rule()` yourself; it is used internally by the parser that implements
_rs_, `rule_parser`.
Here is the definition of the macro that is expanded for each rule:
[define_rule_definition]
Now that we have the `doubles` parser, we can use it like we might any other
parser:
@@ -2545,8 +2548,6 @@ common use cases for _rs_. Use a _r_ if you want to:
* create a set of mutually-recursive parsers;
* separately compile a parser; or
* do callback parsing.
Let's look at the use cases in detail.
@@ -2856,40 +2857,6 @@ strings, etc. Since we need to be able to parse objects within arrays and
vice versa, we need each of those two parsers to be able to refer to each
other.
[heading Separately compiled rules]
_Parser_ is obviously very template-heavy. However, the template parameters
used in your in _Parser_ code are all known at the time you write your rules
and parsers _emdash_ except for the range you're parsing.
Therefore, if you know you're only going to parse with a single kind of range,
you can declare a function that does the parsing, and put the implementation
in a .cpp file. I know. A Boost author talking about putting code in
non-header files is weird. Let's see an example; first, we forward declare
our parse function in a header.
// foo.h
bool balanced_parens(std::string_view str);
Then we define the function, plus all the rules and parsers it uses, in an
implementation file:
// foo.cpp
namespace {
namespace bp = boost::parser;
bp::rule<struct parens_tag> parens = "matched parentheses";
auto const parens_def = ('(' >> parens > ')') | bp::eps;
BOOST_PARSER_DEFINE_RULES(parens);
}
bool balanced_parens(std::string_view str)
{
namespace bp = boost::parser;
return bp::parse(str, bp::omit[parens], bp::ws);
}
For large parsers, separate compilation can be a significant savings on build
time.
[heading Callback parsing]
Only _rs_ can be callback parsers, so if you want to get attributes supplied
@@ -4001,18 +3968,6 @@ Some things to note:
The error will be "Expected '"', '\', '/', 'b', 'f', 'n', 'r', or 't' here",
which is pretty helpful.
[heading Compile separately when you know the type of your input will not change]
If your input type will not change (for instance, if you always parse from a
`std::string` and nothing else), you can use separate compilation to keep from
recompiling your parsing code over and over in every translation unit that
includes it. For instance, in the JSON callback parser example, there is a
call to `json::parse()`, which is a template. However, the function template
is always instantiated with the same parameter: `json_callbacks`, a type
defined in the example. It would be possible to remove the template parameter
from `json::parse()`, forward declare `json_callbacks` and `json::parse()`,
and define them in a different implementation file.
[heading Have a simple test that you can run to find ill-formed-code-as-asserts]
Most of these errors are found at parser construction time, so no actual

View File

@@ -5310,7 +5310,8 @@ namespace boost { namespace parser {
#ifndef BOOST_PARSER_DOXYGEN
#define BOOST_PARSER_DEFINE_IMPL(_, diagnostic_text_) \
//[ define_rule_definition
#define BOOST_PARSER_DEFINE_IMPL(_, rule_name_) \
template< \
bool UseCallbacks, \
typename Iter, \
@@ -5318,7 +5319,7 @@ namespace boost { namespace parser {
typename Context, \
typename SkipParser> \
auto parse_rule( \
decltype(diagnostic_text_)::parser_type::tag_type *, \
decltype(rule_name_)::parser_type::tag_type *, \
std::bool_constant<UseCallbacks> use_cbs, \
Iter & first, \
Sentinel last, \
@@ -5327,7 +5328,7 @@ namespace boost { namespace parser {
boost::parser::detail::flags flags, \
bool & success) \
{ \
auto const & parser = BOOST_PARSER_PP_CAT(diagnostic_text_, _def); \
auto const & parser = BOOST_PARSER_PP_CAT(rule_name_, _def); \
return parser(use_cbs, first, last, context, skip, flags, success); \
} \
\
@@ -5339,7 +5340,7 @@ namespace boost { namespace parser {
typename SkipParser, \
typename Attribute> \
void parse_rule( \
decltype(diagnostic_text_)::parser_type::tag_type *, \
decltype(rule_name_)::parser_type::tag_type *, \
std::bool_constant<UseCallbacks> use_cbs, \
Iter & first, \
Sentinel last, \
@@ -5349,7 +5350,7 @@ namespace boost { namespace parser {
bool & success, \
Attribute & retval) \
{ \
auto const & parser = BOOST_PARSER_PP_CAT(diagnostic_text_, _def); \
auto const & parser = BOOST_PARSER_PP_CAT(rule_name_, _def); \
using attr_t = decltype(parser( \
use_cbs, first, last, context, skip, flags, success)); \
if constexpr (boost::parser::detail::is_nope_v<attr_t>) { \
@@ -5359,12 +5360,15 @@ namespace boost { namespace parser {
use_cbs, first, last, context, skip, flags, success, retval); \
} \
}
//]
#endif
/** For each given token `t`, defines a pair of `parse_rule()` overloads,
used internally within Boost.Parser. Each such pair implements the
parsing behavior rule `t`, using the parser `t_def`. */
parsing behavior rule `t`, using the parser `t_def`. This
implementation is in the form of a pair of function templates. You
should therefore write this macro only at namespace scope. */
#define BOOST_PARSER_DEFINE_RULES(...) \
BOOST_PARSER_PP_FOR_EACH(BOOST_PARSER_DEFINE_IMPL, _, __VA_ARGS__)