2
0
mirror of https://github.com/boostorg/parser.git synced 2026-02-22 03:32:14 +00:00

16 Commits

Author SHA1 Message Date
Andreas Buhr
4a200a4074 Add define BOOST_PARSER_DISABLE_TRACE to disable trace mode at compile time.
The trace feature doubles the compile time, even if never used.
This patch introduces the preprocessor define BOOST_PARSER_DISABLE_TRACE
to deactivate this feature at compile time.
2026-02-15 15:18:22 -06:00
Andreas Buhr
1b984e3546 Allow move-only attributes
This tests the usage of move-only attributes.
parser.hpp and printing.hpp are slightly modified to make it work.
2026-02-14 13:45:05 -06:00
Andreas Buhr
316b4921a9 Move values into detail::assign
Code cleanup:
Most of the time, values are std::moved into detail::assign,
but not always. This patch makes usage more consistent to
always use std::move, except for ints, floats, and iterators.
2026-02-14 13:42:09 -06:00
Andreas Buhr
4706f57c8e Avoid creation of temp_result object in seq_parser
The object is never used, only its type is used.
2026-02-14 13:35:49 -06:00
Andreas Buhr
5625f0345e Use std::make_move_iterator in move_back to actually move stuff
The old implementation did not move.
2026-02-14 13:34:29 -06:00
Andreas Buhr
a2df985cc5 fix MacOS CI badge in README.md 2026-02-14 13:32:24 -06:00
Zach Laine
41d939b2bc In the out-argument overload of rule_parser::call(), always parse into a local
attr_type attribute ('attr'), rather than the one supplied ('retval').  Then,
take the parsed attribute and either combine it with retval (if they are both
containers), or assign attr to retval (if attr_type is not none).

Reproducer by Andreas Buhr.

Fixes #294.
2026-01-30 19:16:34 -06:00
Zach Laine
72b8068867 Add missing operand to throw statement.
Fixes #289.
2026-01-25 17:35:08 -06:00
Andreas Buhr
f6c5de7e76 remove CMAKE_BUILD_TYPE from Windows CI configuration
For Visual Studio, cmake generates just one vcxproj for all configurations.
The presence of CMAKE_BUILD_TYPE on the command line leads to a warning.
2026-01-25 16:06:21 -06:00
Andreas Buhr
c54307b95c update CI from macos-13 to macos-15
MacOS 13 is retired in github actions, see
https://github.com/actions/runner-images/issues/13046
2025-12-11 19:32:36 -06:00
Andreas Buhr
5b3f122aa7 use fold expressions in or_parser and seq_parser 2025-12-05 23:48:02 -06:00
Andreas Buhr
6db68bc7f7 add test for constexpr switch_parser
The test led to the following error before:
github_issues.cpp(522): error C3615: constexpr function
'boost::parser::switch_' cannot result in a constant expression
2025-11-25 12:02:18 -06:00
Andreas Buhr
00fe33d2f3 add "constexpr" to switch_parser constructor
so switch(predicate)(valA, parserA)(valB, parserB)
can result in a constexpr object.
2025-11-25 12:02:18 -06:00
Andreas Buhr
bb183cf5ee Improve error visualization.
If the parsed string contains tab characters on the failing line,
the underlining in the error message pointed to the wrong position.

This patch adds as many tab characters to the underlining as
there are in the parsed line. So the underlining points to the
correct position.
2025-11-23 12:14:55 -06:00
Zach Laine
885595f7bd Parse the attribute of quoted_string into a temporary, and assign at the end,
if successful.  This makes the code well-formed when the attribute type is an
optional.

Addresses PR #290.
2025-11-15 16:39:34 -06:00
Zach Laine
6826f957a1 Add missing 'else' between two if constexpr cases. 2025-11-15 15:57:05 -06:00
14 changed files with 551 additions and 54 deletions

View File

@@ -1,4 +1,4 @@
name: macos-13 - Clang 14
name: macos-15 - Clang
on:
push:
@@ -14,9 +14,9 @@ jobs:
build:
strategy:
matrix:
cxx_std: [17]
cxx_std: [17, 20]
runs-on: macos-13
runs-on: macos-15
steps:
- uses: actions/checkout@v4

View File

@@ -18,6 +18,13 @@ jobs:
compiler_version: [g++-10, g++-11]
cxx_std: [17, 20]
os: [ubuntu-22.04]
disable_trace: [false]
include:
# Test with trace disabled
- compiler_version: g++-11
cxx_std: 20
os: ubuntu-22.04
disable_trace: true
runs-on: ${{ matrix.os }}
@@ -30,7 +37,11 @@ jobs:
run: |
mkdir build
cd build
cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler_version }} -DCXX_STD=${{ matrix.cxx_std }}
if [ "${{ matrix.disable_trace }}" = "true" ]; then
cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler_version }} -DCXX_STD=${{ matrix.cxx_std }} -DDISABLE_TRACE=true
else
cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler_version }} -DCXX_STD=${{ matrix.cxx_std }}
fi
- name: Build
run: cd build ; make -j4

View File

@@ -17,6 +17,12 @@ jobs:
matrix:
cxx_std: [17, 20, 23]
os: [windows-2022]
disable_trace: [false]
include:
# Test with trace disabled
- cxx_std: 20
os: windows-2022
disable_trace: true
runs-on: ${{ matrix.os }}
@@ -24,13 +30,19 @@ jobs:
- uses: actions/checkout@v4
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# Configure CMake in a 'build' subdirectory. Visual Studio is a multi-config generator, so we don't use CMAKE_BUILD_TYPE.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCXX_STD=${{ matrix.cxx_std }}
run: |
if ("${{ matrix.disable_trace }}" -eq "true") {
cmake -B build -DCXX_STD=${{ matrix.cxx_std }} -DDISABLE_TRACE=true
} else {
cmake -B build -DCXX_STD=${{ matrix.cxx_std }}
}
shell: pwsh
- name: Build
working-directory: build
run: cmake --build . -- /p:CL_MPcount=4
run: cmake --build . --config ${{ env.BUILD_TYPE }} -- /p:CL_MPcount=4
- name: Test
working-directory: build/test

View File

@@ -64,6 +64,12 @@ if (BUILD_WITH_HANA)
add_definitions(-DBOOST_PARSER_USE_HANA_TUPLE)
endif()
set(DISABLE_TRACE false CACHE BOOL
"Disable parser trace functionality (defines BOOST_PARSER_DISABLE_TRACE).")
if (DISABLE_TRACE)
add_definitions(-DBOOST_PARSER_DISABLE_TRACE)
endif()
##################################################
# Dependencies

View File

@@ -67,6 +67,6 @@ Develop status:
[![Windows MSVC](https://github.com/tzlaine/parser/actions/workflows/windows.yml/badge.svg?branch=develop)](https://github.com/tzlaine/parser/actions/workflows/windows.yml)
[![macos-13 - Clang 14](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml/badge.svg?branch=develop)](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml)
[![macos-15 - Clang](https://github.com/tzlaine/parser/actions/workflows/macos-15.yml/badge.svg?branch=develop)](https://github.com/tzlaine/parser/actions/workflows/macos-15.yml)
[![License](https://img.shields.io/badge/license-boost-brightgreen.svg)](LICENSE_1_0.txt)

View File

@@ -148,6 +148,7 @@
[def _RULES_ [macroref BOOST_PARSER_DEFINE_RULES `BOOST_PARSER_DEFINE_RULES`]]
[def _AGGR_SIZE_ [macroref BOOST_PARSER_MAX_AGGREGATE_SIZE `BOOST_PARSER_MAX_AGGREGATE_SIZE`]]
[def _SUBRNG_ [macroref BOOST_PARSER_SUBRANGE `BOOST_PARSER_SUBRANGE`]]
[def _DISABLE_TRACE_ [macroref BOOST_PARSER_DISABLE_TRACE `BOOST_PARSER_DISABLE_TRACE`]]
[def __p_ [globalref boost::parser::_p `_p`]]

View File

@@ -3766,6 +3766,22 @@ Some things to be aware of when looking at _Parser_ trace output:
produces that value. In these cases, you'll see the resolved value of the
parse argument.
[heading Compile-time trace disabling]
While trace mode is very useful for debugging, it does have some overhead.
Most parser templates are instantiated twice: Once with tracing enabled,
once with tracing disabled.
If you want to completely disable trace functionality at compile time, you can define
`BOOST_PARSER_DISABLE_TRACE` before including any Boost.Parser headers:
[teletype]``
#define BOOST_PARSER_DISABLE_TRACE
#include <boost/parser/parser.hpp>
``
When this define is set, all trace-related code is compiled out,
reducing the compile time.
[endsect]
[section Memory Allocation]
@@ -3983,7 +3999,8 @@ This defines a RAII trace object that will produce the verbose trace requested
by the user if they passed `_trace_::on` to the top-level parse. It only has
effect if `detail::enable_trace(flags)` is `true`. If trace is enabled, it
will show the state of the parse at the point at which it is defined, and then
again when it goes out of scope.
again when it goes out of scope. By defining `BOOST_PARSER_DISABLE_TRACE`,
the trace feature can be disabled globally at compile time.
[important For the tracing code to work, you must define an overload of
`detail::print_parser` for your new parser type/template. See

View File

@@ -36,6 +36,10 @@
disable the use of concepts, define this macro. */
# define BOOST_PARSER_DISABLE_CONCEPTS
/** Boost.Parser will generate code to trace the execution of each and every
parser by default. To disable all trace code, define this macro. */
# define BOOST_PARSER_DISABLE_TRACE
/** Define this macro to use `boost::hana::tuple` instead of `std::tuple`
throughout Boost.Parser. */
# define BOOST_PARSER_USE_HANA_TUPLE
@@ -92,6 +96,12 @@
# define BOOST_PARSER_USE_CONCEPTS 0
#endif
#if defined(BOOST_PARSER_DISABLE_TRACE)
# define BOOST_PARSER_DO_TRACE 0
#else
# define BOOST_PARSER_DO_TRACE 1
#endif
#if defined(__cpp_lib_ranges) && BOOST_PARSER_USE_CONCEPTS
# define BOOST_PARSER_SUBRANGE std::ranges::subrange
#else

View File

@@ -593,11 +593,14 @@ namespace boost { namespace parser { namespace detail {
}
template<typename Context, typename T>
auto resolve(Context const & context, T const & x);
decltype(auto) resolve(Context const & context, T const & x);
template<typename Context>
auto resolve(Context const &, nope n);
template<typename DependentType, bool DoTraceMacro>
constexpr bool trace_disabled = !DoTraceMacro;
template<
bool DoTrace,
typename Iter,
@@ -606,6 +609,8 @@ namespace boost { namespace parser { namespace detail {
typename Attribute>
struct scoped_trace_t
{
static_assert(!trace_disabled<Iter, BOOST_PARSER_DO_TRACE>);
scoped_trace_t(
std::ostream & os,
Iter & first,
@@ -681,6 +686,9 @@ namespace boost { namespace parser { namespace detail {
flags f,
Attribute const & attr)
{
if constexpr (!BOOST_PARSER_DO_TRACE)
return;
if constexpr (Context::do_trace) {
std::stringstream oss;
detail::print_parser(context, parser, oss);
@@ -695,6 +703,9 @@ namespace boost { namespace parser { namespace detail {
template<typename Context, typename Attribute>
auto final_trace(Context const & context, flags f, Attribute const & attr)
{
if constexpr (!BOOST_PARSER_DO_TRACE)
return;
if (!detail::do_trace(f))
return;

View File

@@ -90,6 +90,9 @@ namespace boost { namespace parser {
os << ":\n";
std::string underlining(std::distance(position.line_start, it), ' ');
std::transform(position.line_start, it,
underlining.begin(),
[](auto c) { return c == '\t' ? '\t' : ' ';});
detail::trace_input(os, position.line_start, it, false, 1u << 31);
if (it == last) {
os << '\n' << underlining << "^\n";

View File

@@ -1020,7 +1020,7 @@ namespace boost { namespace parser {
bool Callable = is_detected_v<callable, T const &, Context const &>>
struct resolve_impl
{
static auto call(Context const &, T const & x) { return x; }
static auto& call(Context const &, T const & x) { return x; }
};
template<typename Context, typename T>
@@ -1033,7 +1033,7 @@ namespace boost { namespace parser {
};
template<typename Context, typename T>
auto resolve(Context const & context, T const & x)
decltype(auto) resolve(Context const & context, T const & x)
{
return resolve_impl<Context, T>::call(context, x);
}
@@ -2168,7 +2168,10 @@ namespace boost { namespace parser {
{
if (!gen_attrs || !x)
return;
c.insert(c.end(), x->begin(), x->end());
c.insert(c.end(),
std::make_move_iterator(x->begin()),
std::make_move_iterator(x->end())
);
}
template<typename Container, typename T>
@@ -2851,7 +2854,7 @@ namespace boost { namespace parser {
last,
parse_error<I>(first, "end of input")) ==
error_handler_result::rethrow) {
throw;
throw parse_error<I>(first, "end of input");
}
if constexpr (std::is_same_v<T, bool>)
retval = false;
@@ -3177,6 +3180,7 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this,
first,
@@ -3185,6 +3189,7 @@ namespace boost { namespace parser {
detail::in_apply_parser(flags) ? detail::disable_trace(flags)
: flags,
retval);
#endif
if constexpr (detail::is_optional_v<Attribute>) {
detail::optional_type<Attribute> attr;
@@ -3341,8 +3346,10 @@ namespace boost { namespace parser {
Attribute & retval) const
{
//[ opt_parser_trace
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
//]
//[ opt_parser_skip
@@ -3505,8 +3512,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
use_parser_t<Iter, Sentinel, Context, SkipParser> const use_parser{
first, last, context, skip, flags, success};
@@ -3529,7 +3538,9 @@ namespace boost { namespace parser {
else
use_parser.first_ = prev_first;
};
detail::hl::for_each(parsers_, try_parser); // TODO: -> fold-expr
std::apply([&try_parser](auto&&... args) {
((try_parser(args)), ...);
}, parsers_);
if (!done)
success = false;
@@ -3627,8 +3638,10 @@ namespace boost { namespace parser {
decltype(detail::hl::transform(parsers_, use_parser));
result_t retval{};
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first_, last, context, flags, retval);
#endif
call_impl(
first,
@@ -3663,8 +3676,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first_, last, context, flags, retval);
#endif
Iter first = first_;
use_parser_t<Iter, Sentinel, Context, SkipParser> const use_parser{
@@ -3711,7 +3726,7 @@ namespace boost { namespace parser {
if constexpr (detail::is_struct_compatible_v<
Attribute,
result_t>) {
detail::assign(retval, temp_retval);
detail::assign(retval, std::move(temp_retval));
} else {
detail::assign(
retval,
@@ -4290,12 +4305,13 @@ namespace boost { namespace parser {
{
Iter first = first_;
auto temp_result =
make_temp_result(first, last, context, skip, flags, success);
using temp_result_t =
decltype(make_temp_result(first, last, context, skip, flags, success));
std::decay_t<decltype(parser::get(temp_result, llong<0>{}))>
std::decay_t<decltype(parser::get(std::declval<temp_result_t>(), llong<0>{}))>
retval{};
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this,
first_,
@@ -4304,10 +4320,11 @@ namespace boost { namespace parser {
detail::in_apply_parser(flags) ? detail::disable_trace(flags)
: flags,
retval);
#endif
std::decay_t<decltype(parser::get(temp_result, llong<1>{}))>
std::decay_t<decltype(parser::get(std::declval<temp_result_t>(), llong<1>{}))>
indices;
std::decay_t<decltype(parser::get(temp_result, llong<2>{}))>
std::decay_t<decltype(parser::get(std::declval<temp_result_t>(), llong<2>{}))>
merged;
call_impl(
first,
@@ -4326,7 +4343,7 @@ namespace boost { namespace parser {
// A 1-tuple is converted to a scalar.
if constexpr (detail::hl::size(retval) == llong<1>{}) {
using namespace literals;
return parser::get(retval, 0_c);
return parser::get(std::move(retval), 0_c);
} else {
return retval;
}
@@ -4347,6 +4364,7 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this,
first_,
@@ -4355,16 +4373,17 @@ namespace boost { namespace parser {
detail::in_apply_parser(flags) ? detail::disable_trace(flags)
: flags,
retval);
#endif
Iter first = first_;
auto temp_result =
make_temp_result(first, last, context, skip, flags, success);
using temp_result_t =
decltype(make_temp_result(first, last, context, skip, flags, success));
using temp_result_attr_t =
std::decay_t<decltype(parser::get(temp_result, llong<0>{}))>;
std::decay_t<decltype(parser::get(temp_result, llong<1>{}))>
std::decay_t<decltype(parser::get(std::declval<temp_result_t>(), llong<0>{}))>;
std::decay_t<decltype(parser::get(std::declval<temp_result_t>(), llong<1>{}))>
indices;
std::decay_t<decltype(parser::get(temp_result, llong<2>{}))> merged;
std::decay_t<decltype(parser::get(std::declval<temp_result_t>(), llong<2>{}))> merged;
auto max_ = [](auto result, auto x) {
if constexpr (decltype(result)::value < decltype(x)::value) {
@@ -4562,7 +4581,7 @@ namespace boost { namespace parser {
using just_out = detail::remove_cv_ref_t<decltype(out)>;
if constexpr (detail::is_nope_v<attr_t>) {
// nothing to do
} if constexpr (
} else if constexpr (
(!out_container ||
!std::is_same_v<just_x, just_out>) &&
std::is_assignable_v<just_out &, just_x &&> &&
@@ -4578,7 +4597,9 @@ namespace boost { namespace parser {
auto const parsers_and_indices =
detail::hl::zip(parsers_, indices, merged, backtracking{});
detail::hl::for_each(parsers_and_indices, use_parser);
std::apply([&use_parser](auto&&... args) {
((use_parser(args)), ...);
}, parsers_and_indices);
}
template<bool AllowBacktracking, typename Parser>
@@ -4699,8 +4720,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
auto const initial_first = first;
auto attr = parser_.call(
@@ -4765,8 +4788,10 @@ namespace boost { namespace parser {
detail::flags flags,
bool & success) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, detail::global_nope);
#endif
auto attr =
parser_.call(first, last, context, skip, flags, success);
if (success && detail::gen_attrs(flags))
@@ -4790,8 +4815,11 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
auto attr =
parser_.call(first, last, context, skip, flags, success);
if (success && detail::gen_attrs(flags))
@@ -4818,8 +4846,10 @@ namespace boost { namespace parser {
detail::flags flags,
bool & success) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, detail::global_nope);
#endif
parser_.call(
first,
@@ -4846,8 +4876,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
parser_.call(
first,
@@ -4897,8 +4929,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
auto const initial_first = first;
parser_.call(
@@ -4962,8 +4996,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
auto const initial_first = first;
parser_.call(
@@ -5038,8 +5074,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
parser_.call(
first,
@@ -5098,8 +5136,10 @@ namespace boost { namespace parser {
auto context = context_;
++context.no_case_depth_;
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
parser_.call(first, last, context, skip, flags, success, retval);
}
@@ -5145,8 +5185,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
if constexpr (detail::is_nope_v<SkipParser>) {
parser_.call(
@@ -5209,8 +5251,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
auto first_copy = first;
parser_.call(
@@ -5376,8 +5420,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
auto [trie, _0] = detail::get_trie(context, ref());
auto const lookup = context.no_case_depth_
@@ -5451,8 +5497,10 @@ namespace boost { namespace parser {
tag_type * const tag_ptr = nullptr;
auto const rule_context = detail::make_rule_context(
context, tag_ptr, retval, locals, params);
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, rule_context, flags, retval);
#endif
bool dont_assign = false;
if constexpr (in_recursion) {
@@ -5481,7 +5529,7 @@ namespace boost { namespace parser {
dont_assign);
if (success && !dont_assign) {
if constexpr (!detail::is_nope_v<decltype(attr)>)
detail::assign(retval, attr);
detail::assign(retval, std::move(attr));
}
}
@@ -5545,11 +5593,14 @@ namespace boost { namespace parser {
locals_type locals = detail::make_locals<locals_type>(context);
auto params = detail::resolve_rule_params(context, params_);
tag_type * const tag_ptr = nullptr;
attr_type attr{};
auto const rule_context = detail::make_rule_context(
context, tag_ptr, retval, locals, params);
context, tag_ptr, attr, locals, params);
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, rule_context, flags, retval);
#endif
bool dont_assign = false;
parse_rule(
@@ -5561,9 +5612,22 @@ namespace boost { namespace parser {
flags,
success,
dont_assign,
retval);
if (!success || dont_assign)
retval = Attribute_();
attr);
if (dont_assign)
return;
if (!success)
attr = attr_type{};
if constexpr (detail::is_nope_v<decltype(attr)>) {
return;
} else if constexpr (
container<Attribute_> && container<attr_type>) {
detail::move_back(retval, attr, detail::gen_attrs(flags));
} else {
detail::assign(retval, std::move(attr));
}
}
}
@@ -6726,8 +6790,10 @@ namespace boost { namespace parser {
detail::flags flags,
bool & success) const noexcept
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, detail::global_nope);
#endif
BOOST_PARSER_SUBRANGE const where(first, first);
auto const predicate_context = detail::make_action_context(
context, detail::global_nope, where);
@@ -6754,8 +6820,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
BOOST_PARSER_SUBRANGE const where(first, first);
auto const predicate_context = detail::make_action_context(
context, detail::global_nope, where);
@@ -6805,8 +6873,10 @@ namespace boost { namespace parser {
detail::flags flags,
bool & success) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, detail::global_nope);
#endif
if (first != last)
success = false;
return {};
@@ -6827,8 +6897,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
if (first != last)
success = false;
}
@@ -6857,8 +6929,10 @@ namespace boost { namespace parser {
detail::flags flags,
bool &) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, detail::global_nope);
#endif
return detail::resolve(context, attr_);
}
@@ -6877,8 +6951,10 @@ namespace boost { namespace parser {
bool & success,
Attribute_ & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
if (detail::gen_attrs(flags))
detail::assign_copy(retval, detail::resolve(context, attr_));
}
@@ -6943,8 +7019,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
if (first == last) {
success = false;
@@ -7108,8 +7186,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
if (first == last) {
success = false;
@@ -7259,8 +7339,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
if (first == last) {
success = false;
@@ -7339,8 +7421,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
if (first == last) {
success = false;
@@ -7461,8 +7545,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
if (first == last) {
success = false;
@@ -7625,8 +7711,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
if (first == last) {
success = false;
@@ -7635,9 +7723,10 @@ namespace boost { namespace parser {
auto const prev_first = first;
auto append = [&retval,
std::string temp;
auto append = [&temp,
gen_attrs = detail::gen_attrs(flags)](auto & ctx) {
detail::move_back(retval, _attr(ctx), gen_attrs);
detail::move_back(temp, _attr(ctx), gen_attrs);
};
auto quote_ch = [&]() {
@@ -7695,7 +7784,10 @@ namespace boost { namespace parser {
detail::disable_skip(flags),
success);
if (!success) {
if (success) {
if (detail::gen_attrs(flags))
detail::assign(retval, std::move(temp));
} else {
retval = Attribute();
first = prev_first;
}
@@ -7942,8 +8034,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
if (first == last) {
success = false;
@@ -8109,8 +8203,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
auto compare =
[no_case = context.no_case_depth_](char32_t a, char32_t b) {
@@ -8198,8 +8294,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
T attr = 0;
auto const initial = first;
success =
@@ -8339,8 +8437,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
T attr = 0;
auto const initial = first;
success =
@@ -8454,8 +8554,10 @@ namespace boost { namespace parser {
bool & success,
Attribute & retval) const
{
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
T attr = 0;
auto const initial = first;
success = detail::numeric::parse_real(first, last, attr);
@@ -8521,9 +8623,9 @@ namespace boost { namespace parser {
template<typename SwitchValue, typename OrParser>
struct switch_parser
{
switch_parser() {}
switch_parser(SwitchValue switch_value) : switch_value_(switch_value) {}
switch_parser(SwitchValue switch_value, OrParser or_parser) :
constexpr switch_parser() {}
constexpr switch_parser(SwitchValue switch_value) : switch_value_(switch_value) {}
constexpr switch_parser(SwitchValue switch_value, OrParser or_parser) :
switch_value_(switch_value), or_parser_(or_parser)
{}
@@ -8548,8 +8650,10 @@ namespace boost { namespace parser {
using attr_t = decltype(or_parser_.call(
first, last, context, skip, flags, success));
attr_t attr{};
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ =
detail::scoped_trace(*this, first, last, context, flags, attr);
#endif
attr = or_parser_.call(first, last, context, skip, flags, success);
return attr;
}
@@ -8574,8 +8678,10 @@ namespace boost { namespace parser {
"It looks like you tried to write switch_(val). You need at "
"least one alternative, like: switch_(val)(value_1, "
"parser_1)(value_2, parser_2)..."));
#if BOOST_PARSER_DO_TRACE
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
#endif
or_parser_.call(first, last, context, skip, flags, success, retval);
}
@@ -9034,8 +9140,13 @@ namespace boost { namespace parser {
"fill in attr above, using the attribute generated by parser. "
"However, parser does not generate an attribute.");
if (trace_mode == trace::on) {
return reset = detail::parse_impl<true>(
first, last, parser, parser.error_handler_, attr);
return reset =
detail::parse_impl<(true && BOOST_PARSER_DO_TRACE)>(
first,
last,
parser,
parser.error_handler_,
attr);
} else {
return reset = detail::parse_impl<false>(
first, last, parser, parser.error_handler_, attr);
@@ -9052,8 +9163,9 @@ namespace boost { namespace parser {
"fill in attr above, using the attribute generated by parser. "
"However, parser does not generate an attribute.");
if (trace_mode == trace::on) {
return reset = detail::parse_impl<true>(
f, l, parser, parser.error_handler_, attr);
return reset =
detail::parse_impl<(true && BOOST_PARSER_DO_TRACE)>(
f, l, parser, parser.error_handler_, attr);
} else {
return reset = detail::parse_impl<false>(
f, l, parser, parser.error_handler_, attr);
@@ -9156,7 +9268,7 @@ namespace boost { namespace parser {
{
if constexpr (!detail::is_char8_iter_v<I>) {
if (trace_mode == trace::on) {
return detail::parse_impl<true>(
return detail::parse_impl<(true && BOOST_PARSER_DO_TRACE)>(
first, last, parser, parser.error_handler_);
} else {
return detail::parse_impl<false>(
@@ -9169,7 +9281,7 @@ namespace boost { namespace parser {
auto const l = r.end();
auto _ = detail::scoped_base_assign(first, f);
if (trace_mode == trace::on) {
return detail::parse_impl<true>(
return detail::parse_impl<(true && BOOST_PARSER_DO_TRACE)>(
f, l, parser, parser.error_handler_);
} else {
return detail::parse_impl<false>(
@@ -9275,7 +9387,8 @@ namespace boost { namespace parser {
"fill in attr above, using the attribute generated by parser. "
"However, parser does not generate an attribute.");
if (trace_mode == trace::on) {
return reset = detail::skip_parse_impl<true>(
return reset = detail::skip_parse_impl<(
true && BOOST_PARSER_DO_TRACE)>(
first,
last,
parser,
@@ -9303,7 +9416,8 @@ namespace boost { namespace parser {
"fill in attr above, using the attribute generated by parser. "
"However, parser does not generate an attribute.");
if (trace_mode == trace::on) {
return reset = detail::skip_parse_impl<true>(
return reset = detail::skip_parse_impl<(
true && BOOST_PARSER_DO_TRACE)>(
f, l, parser, skip, parser.error_handler_, attr);
} else {
return reset = detail::skip_parse_impl<false>(
@@ -9408,7 +9522,7 @@ namespace boost { namespace parser {
{
if constexpr (!detail::is_char8_iter_v<I>) {
if (trace_mode == trace::on) {
return detail::skip_parse_impl<true>(
return detail::skip_parse_impl<(true && BOOST_PARSER_DO_TRACE)>(
first, last, parser, skip, parser.error_handler_);
} else {
return detail::skip_parse_impl<false>(
@@ -9421,7 +9535,7 @@ namespace boost { namespace parser {
auto const l = r.end();
auto _ = detail::scoped_base_assign(first, f);
if (trace_mode == trace::on) {
return detail::skip_parse_impl<true>(
return detail::skip_parse_impl<(true && BOOST_PARSER_DO_TRACE)>(
f, l, parser, skip, parser.error_handler_);
} else {
return detail::skip_parse_impl<false>(
@@ -9525,7 +9639,8 @@ namespace boost { namespace parser {
{
if constexpr (!detail::is_char8_iter_v<I>) {
if (trace_mode == trace::on) {
return detail::callback_parse_impl<true>(
return detail::callback_parse_impl<(
true && BOOST_PARSER_DO_TRACE)>(
first, last, parser, parser.error_handler_, callbacks);
} else {
return detail::callback_parse_impl<false>(
@@ -9538,7 +9653,8 @@ namespace boost { namespace parser {
auto const l = r.end();
auto _ = detail::scoped_base_assign(first, f);
if (trace_mode == trace::on) {
return detail::callback_parse_impl<true>(
return detail::callback_parse_impl<(
true && BOOST_PARSER_DO_TRACE)>(
f, l, parser, parser.error_handler_, callbacks);
} else {
return detail::callback_parse_impl<false>(
@@ -9650,7 +9766,8 @@ namespace boost { namespace parser {
{
if constexpr (!detail::is_char8_iter_v<I>) {
if (trace_mode == trace::on) {
return detail::callback_skip_parse_impl<true>(
return detail::callback_skip_parse_impl<(
true && BOOST_PARSER_DO_TRACE)>(
first,
last,
parser,
@@ -9673,7 +9790,8 @@ namespace boost { namespace parser {
auto const l = r.end();
auto _ = detail::scoped_base_assign(first, f);
if (trace_mode == trace::on) {
return detail::callback_skip_parse_impl<true>(
return detail::callback_skip_parse_impl<(
true && BOOST_PARSER_DO_TRACE)>(
f, l, parser, skip, parser.error_handler_, callbacks);
} else {
return detail::callback_skip_parse_impl<false>(

View File

@@ -82,6 +82,7 @@ add_test_executable(parser_seq_permutations_1)
add_test_executable(parser_seq_permutations_2)
add_test_executable(parser_or_permutations_1)
add_test_executable(parser_or_permutations_2)
add_test_executable(disable_trace)
if (MSVC)
add_executable(vs_output_tracing tracing.cpp)

27
test/disable_trace.cpp Normal file
View File

@@ -0,0 +1,27 @@
/**
* Copyright (C) 2025
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
#define BOOST_PARSER_DISABLE_TRACE
#include <boost/core/lightweight_test.hpp>
#include <boost/parser/parser.hpp>
int main()
{
namespace bp = boost::parser;
{
auto const parser =
bp::string("FOO") >> -(bp::string("bar") | bp::string("foo"));
auto result = bp::parse("FOOfoo", parser);
BOOST_TEST(result);
BOOST_TEST(bp::get(*result, bp::llong<0>{}) == std::string("FOO"));
BOOST_TEST(bp::get(*result, bp::llong<1>{}) == std::string("foo"));
}
return boost::report_errors();
}

View File

@@ -501,6 +501,282 @@ void github_issue_285()
BOOST_TEST(result.value().get() != nullptr);
}
void github_pr_290()
{
namespace bp = boost::parser;
auto const pTest = bp::lit("TEST:") > -bp::quoted_string;
auto result = bp::parse("TEST: \"foo\"", pTest, bp::blank);
BOOST_TEST(result);
BOOST_TEST(*result == "foo");
}
namespace github_issue_294_ {
namespace bp = boost::parser;
struct Foo
{};
constexpr bp::rule<struct foo_parser_tag, std::shared_ptr<Foo>> foo_parser =
"foo_parser";
constexpr auto foo_parser_action = [](auto & ctx) {
std::shared_ptr<Foo> & val = _val(ctx);
val = std::shared_ptr<Foo>(new Foo{});
};
constexpr auto foo_parser_def = bp::eps[foo_parser_action];
struct Bar
{
std::shared_ptr<Foo> foo;
};
constexpr bp::rule<struct bar_parser_tag, std::shared_ptr<Bar>> bar_parser =
"bar_parser";
constexpr auto bar_parser_action = [](auto & ctx) {
std::shared_ptr<Bar> & val = _val(ctx);
val = std::shared_ptr<Bar>(new Bar{});
std::optional<std::shared_ptr<Foo>> & attr = _attr(ctx);
if (attr) {
val->foo = attr.value();
}
};
constexpr auto bar_parser_def =
(bp::lit("(") > -foo_parser > bp::lit(")"))[bar_parser_action];
BOOST_PARSER_DEFINE_RULES(bar_parser, foo_parser);
}
void github_issue_294()
{
namespace bp = boost::parser;
using namespace github_issue_294_;
bp::parse("()", bar_parser, bp::blank);
}
namespace github_pr_297_ {
namespace bp = boost::parser;
constexpr auto bar_required_f = [](auto& ctx) -> bool {
const bool& argument = bp::_p<0>(ctx);
return argument;
};
constexpr bp::rule<struct foobar_parser_tag> foobar_parser = "foobar_parser";
constexpr auto foobar_parser_def =
bp::lit("foo")
>> bp::switch_(bar_required_f)
(true, bp::lit("bar"))
(false, -bp::lit("bar"));
BOOST_PARSER_DEFINE_RULES(foobar_parser);
}
void github_pr_297()
{
namespace bp = boost::parser;
using namespace github_pr_297_;
{
const bool bar_required = true;
const bool result =
bp::parse("foo bar", foobar_parser.with(bar_required), bp::blank);
BOOST_TEST(result);
}
{
const bool bar_required = true;
const bool result =
bp::parse("foo", foobar_parser.with(bar_required), bp::blank);
BOOST_TEST(!result);
}
{
const bool bar_required = false;
const bool result =
bp::parse("foo bar", foobar_parser.with(bar_required), bp::blank);
BOOST_TEST(result);
}
{
const bool bar_required = false;
const bool result =
bp::parse("foo", foobar_parser.with(bar_required), bp::blank);
BOOST_TEST(result);
}
}
namespace github_issue_312_ {
/*
* Recursive descent parser for expressions.
* Supports addition (+), multiplication (*) and
* parethesized expressions, nothing else.
*
* Creates a tree of "evaluatable" objects which
* own their downstream objects in a unique_ptr
*/
// base class for all tree nodes
struct evaluatable
{
virtual double evaluate() = 0;
virtual ~evaluatable() = default;
};
namespace bp = boost::parser;
// top level parser
constexpr bp::rule<struct expression_tag, std::unique_ptr<evaluatable>> expression_parser = "expression_parser";
/*
* LITERAL EXPRESSION
*/
struct literal_evaluatable : evaluatable
{
explicit literal_evaluatable(double v) : value_(v) {}
double evaluate() override
{
return value_;
}
double value_;
};
constexpr bp::rule<struct literal_tag, std::unique_ptr<evaluatable>> literal_parser = "literal_parser";
constexpr auto literal_parser_action = [](auto& ctx) {
std::unique_ptr<evaluatable>& val = _val(ctx);
double& parsed_value = _attr(ctx);
val = std::make_unique<literal_evaluatable>(parsed_value);
};
constexpr auto literal_parser_def =
bp::double_[literal_parser_action];
/*
* PARENTHESIZED EXPRESSION
*/
struct parenthesized_evaluatable : evaluatable
{
explicit parenthesized_evaluatable(std::unique_ptr<evaluatable>&& e) : evaluatable_(std::move(e)) {}
double evaluate() override
{
return evaluatable_->evaluate();
}
std::unique_ptr<evaluatable> evaluatable_;
};
constexpr bp::rule<struct parenthesized_tag, std::unique_ptr<evaluatable>> parenthesized_parser = "parenthesized_parser";
constexpr auto parenthesized_action = [](auto& ctx) {
std::unique_ptr<evaluatable>& val = _val(ctx);
std::unique_ptr<evaluatable>& attr = _attr(ctx);
val = std::make_unique<parenthesized_evaluatable>(std::move(attr));
};
constexpr auto parenthesized_parser_def =
(
bp::lit('(') > expression_parser > bp::lit(')')
)[parenthesized_action];
/*
* ATOM EXPRESSION
*/
struct atom_evaluatable : evaluatable
{
explicit atom_evaluatable(std::unique_ptr<evaluatable>&& e) : evaluatable_(std::move(e)) {}
double evaluate() override
{
return evaluatable_->evaluate();
}
std::unique_ptr<evaluatable> evaluatable_;
};
constexpr bp::rule<struct atom_tag, std::unique_ptr<evaluatable>> atom_parser = "atom_parser";
constexpr auto atom_action = [](auto& ctx) {
std::unique_ptr<evaluatable>& val = _val(ctx);
std::unique_ptr<evaluatable>& attr = _attr(ctx);
val = std::make_unique<atom_evaluatable>(std::move(attr));
};
constexpr auto atom_parser_def =
(
parenthesized_parser
|
literal_parser
)[atom_action];
/*
* MULTIPLICATION EXPRESSION
*/
struct multiplication_evaluatable : evaluatable
{
multiplication_evaluatable(std::vector<std::unique_ptr<evaluatable>>&& e)
: evaluatables_(std::move(e))
{}
double evaluate() override
{
double result = 1;
for (const auto& e : evaluatables_) {
result *= e->evaluate();
}
return result;
}
std::vector<std::unique_ptr<evaluatable>> evaluatables_;
};
constexpr bp::rule<struct mult_tag, std::unique_ptr<evaluatable>> mult_parser = "mult_parser";
constexpr auto mult_parser_action = [](auto& ctx) {
std::unique_ptr<evaluatable>& val = _val(ctx);
std::vector<std::unique_ptr<evaluatable>>& operands = _attr(ctx);
val = std::make_unique<multiplication_evaluatable>(std::move(operands));
};
constexpr auto mult_parser_def =
(atom_parser % bp::lit('*'))[mult_parser_action];
/*
* ADDITION EXPRESSION
*/
struct addition_evaluatable : evaluatable
{
addition_evaluatable(std::vector<std::unique_ptr<evaluatable>>&& e)
: evaluatables_(std::move(e))
{}
double evaluate() override
{
double result = 0;
for (const auto& e : evaluatables_) {
result += e->evaluate();
}
return result;
}
std::vector<std::unique_ptr<evaluatable>> evaluatables_;
};
constexpr bp::rule<struct add_tag, std::unique_ptr<evaluatable>> add_parser = "add_parser";
constexpr auto add_parser_action = [](auto& ctx) {
std::unique_ptr<evaluatable>& val = _val(ctx);
std::vector<std::unique_ptr<evaluatable>>& operands = _attr(ctx);
val = std::make_unique<addition_evaluatable>(std::move(operands));
};
constexpr auto add_parser_def =
(mult_parser % bp::lit('+'))[add_parser_action];
constexpr auto expression_parser_action = [](auto& ctx) {
std::unique_ptr<evaluatable>& val = _val(ctx);
std::unique_ptr<evaluatable>& attr = _attr(ctx);
val = std::move(attr);
};
/*
* EXPRESSION
*/
constexpr auto expression_parser_def =
add_parser[expression_parser_action];
BOOST_PARSER_DEFINE_RULES(
literal_parser,
mult_parser,
add_parser,
expression_parser,
parenthesized_parser,
atom_parser);
}
void github_issue_312()
{
namespace bp = boost::parser;
using namespace github_issue_312_;
auto result = bp::parse("(2 + 3) + 3.1415 * 2", expression_parser, bp::blank);
BOOST_TEST(result);
BOOST_TEST(result.value()->evaluate() == (2 + 3) + 3.1415 * 2);
result = bp::parse("((2*0.1) + 33.) * (2 + 3) + 3.1415 * 2", expression_parser, bp::blank);
BOOST_TEST(result);
BOOST_TEST(result.value()->evaluate() == ((2*0.1) + 33.) * (2 + 3) + 3.1415 * 2);
}
int main()
{
@@ -516,5 +792,9 @@ int main()
github_issue_268();
github_issue_279();
github_issue_285();
github_pr_290();
github_issue_294();
github_pr_297();
github_issue_312();
return boost::report_errors();
}