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:
@@ -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).
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user