mirror of
https://github.com/boostorg/parser.git
synced 2026-01-20 16:52:13 +00:00
Compare commits
6 Commits
xform_repl
...
quoted_str
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3fbd6c849 | ||
|
|
2b4243151a | ||
|
|
63884d2efa | ||
|
|
3487211426 | ||
|
|
c12c512a94 | ||
|
|
8ce60a8e53 |
@@ -3014,6 +3014,10 @@ similar.
|
||||
|
||||
[section Algorithms and Views That Use Parsers]
|
||||
|
||||
Unless otherwise noted, all the algorithms and views are constrained very much
|
||||
like the way the _p_ overloads are. The kinds of ranges, parsers, etc., that
|
||||
they accept are the same.
|
||||
|
||||
[heading _search_]
|
||||
|
||||
As shown in _p_api_, the two patterns of parsing in _Parser_ are whole-parse
|
||||
@@ -3141,15 +3145,16 @@ Unlike _split_v_, _replace_v_ does not produce empty subranges, unless
|
||||
`replacement` is empty.
|
||||
|
||||
namespace bp = boost::parser;
|
||||
auto rng = "XYZaaXYZbaabaXYZXYZ" | bp::replace(bp::lit("XYZ"), "foo");
|
||||
auto card_number = bp::int_ >> bp::repeat(3)['-' >> bp::int_];
|
||||
auto rng = "My credit card number is 1234-5678-9012-3456." | bp::replace(card_number, "XXXX-XXXX-XXXX-XXXX");
|
||||
int count = 0;
|
||||
// Prints foo aa foo baaba foo foo.
|
||||
// Prints My credit card number is XXXX-XXXX-XXXX-XXXX.
|
||||
for (auto subrange : rng) {
|
||||
std::cout << std::string_view(subrange.begin(), subrange.end() - subrange.begin()) << " ";
|
||||
std::cout << std::string_view(subrange.begin(), subrange.end() - subrange.begin());
|
||||
++count;
|
||||
}
|
||||
std::cout << "\n";
|
||||
assert(count == 6);
|
||||
assert(count == 3);
|
||||
|
||||
|
||||
If the iterator types `Ir` and `Ireplacement` for the `r` and `replacement`
|
||||
@@ -3164,13 +3169,13 @@ subranges represented by _replace_v_ is easily joined back into a single
|
||||
range.
|
||||
|
||||
namespace bp = boost::parser;
|
||||
char const str[] = "XYZaaXYZbaabaXYZXYZ";
|
||||
auto rng = str | bp::replace(bp::lit("XYZ"), "foo") | std::views::join;
|
||||
auto card_number = bp::int_ >> bp::repeat(3)['-' >> bp::int_];
|
||||
auto rng = "My credit card number is 1234-5678-9012-3456." | bp::replace(card_number, "XXXX-XXXX-XXXX-XXXX") | std::views::join;
|
||||
std::string replace_result;
|
||||
for (auto ch : rng) {
|
||||
replace_result.push_back(ch);
|
||||
}
|
||||
assert(replace_result == "fooaafoobaabafoofoo");
|
||||
assert(replace_result == "My credit card number is XXXX-XXXX-XXXX-XXXX.");
|
||||
|
||||
Note that we could *not* have written `std::string replace_result(r.begin(),
|
||||
r.end())`. This is ill-formed because the `std::string` range constructor
|
||||
@@ -3184,6 +3189,11 @@ is because the subranges produced are all common ranges, and so if
|
||||
pass very long C-style strings to _replace_ and not pay to see the end until
|
||||
the range is used, don't.
|
||||
|
||||
`ReplacementV` is constrained almost exactly the same as `V`. `V` must model
|
||||
`parsable_range` and `std::ranges::viewable_range`. `ReplacementV` is the
|
||||
same, except that it can also be a `std::ranges::input_range`, whereas `V`
|
||||
must be a `std::ranges::forward_range`.
|
||||
|
||||
You may wonder what happens when you pass a UTF-N range for `r`, and a UTF-M
|
||||
range for `replacement`. What happens in this case is silent transcoding of
|
||||
`replacement` from UTF-M to UTF-N by the _replace_ range adaptor. This
|
||||
|
||||
@@ -44,11 +44,7 @@ namespace boost { namespace parser {
|
||||
//]
|
||||
|
||||
template<typename T>
|
||||
concept utf_pointer =
|
||||
std::is_pointer_v<T> && code_unit<std::iter_value_t<T>>;
|
||||
template<typename T>
|
||||
concept range_like =
|
||||
std::ranges::range<T> || utf_pointer<std::remove_cvref_t<T>>;
|
||||
concept range_like = std::ranges::range<T> || parsable_pointer<T>;
|
||||
|
||||
template<
|
||||
typename I,
|
||||
|
||||
@@ -264,6 +264,13 @@ namespace boost { namespace parser { namespace detail {
|
||||
std::ostream & os,
|
||||
int components = 0);
|
||||
|
||||
template<typename Context, typename I, typename S>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
quoted_string_parser<I, S> const & parser,
|
||||
std::ostream & os,
|
||||
int components = 0);
|
||||
|
||||
template<typename Context, bool NewlinesOnly, bool NoNewlines>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
|
||||
@@ -439,8 +439,7 @@ namespace boost { namespace parser { namespace detail {
|
||||
template<
|
||||
typename Context,
|
||||
typename ResolvedExpected,
|
||||
bool Integral = std::is_integral<ResolvedExpected>{},
|
||||
int SizeofExpected = sizeof(ResolvedExpected)>
|
||||
bool Integral = std::is_integral<ResolvedExpected>{}>
|
||||
struct print_expected_char_impl
|
||||
{
|
||||
static void call(
|
||||
@@ -452,13 +451,17 @@ namespace boost { namespace parser { namespace detail {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Context, typename Expected>
|
||||
struct print_expected_char_impl<Context, Expected, true, 4>
|
||||
template<typename Context>
|
||||
struct print_expected_char_impl<Context, char32_t, true>
|
||||
{
|
||||
static void
|
||||
call(Context const & context, std::ostream & os, Expected expected)
|
||||
call(Context const & context, std::ostream & os, char32_t expected)
|
||||
{
|
||||
std::array<char32_t, 1> cps = {{(char32_t)expected}};
|
||||
if (expected == '\'') {
|
||||
os << "'\\''";
|
||||
return;
|
||||
}
|
||||
std::array<char32_t, 1> cps = {{expected}};
|
||||
auto const r = cps | text::as_utf8;
|
||||
os << "'";
|
||||
for (auto c : r) {
|
||||
@@ -646,6 +649,27 @@ namespace boost { namespace parser { namespace detail {
|
||||
os << "\"";
|
||||
}
|
||||
|
||||
template<typename Context, typename I, typename S>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
quoted_string_parser<I, S> const & parser,
|
||||
std::ostream & os,
|
||||
int components)
|
||||
{
|
||||
os << "quoted_string(";
|
||||
if (parser.chs_.empty()) {
|
||||
detail::print_expected_char_impl<Context, char32_t>::call(
|
||||
context, os, parser.ch_);
|
||||
} else {
|
||||
os << '"';
|
||||
for (auto c : parser.chs_ | text::as_utf8) {
|
||||
detail::print_char(os, c);
|
||||
}
|
||||
os << '"';
|
||||
}
|
||||
os << ')';
|
||||
}
|
||||
|
||||
template<typename Context, bool NewlinesOnly, bool NoNewlines>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
|
||||
@@ -3914,10 +3914,11 @@ namespace boost { namespace parser {
|
||||
last,
|
||||
&context,
|
||||
&skip,
|
||||
flags,
|
||||
flags_ = flags,
|
||||
&success,
|
||||
&retval](auto const &
|
||||
parser_index_merged_and_backtrack) {
|
||||
auto flags = flags_;
|
||||
using namespace literals;
|
||||
detail::skip(first, last, skip, flags);
|
||||
if (!success) // Someone earlier already failed...
|
||||
@@ -3969,6 +3970,9 @@ namespace boost { namespace parser {
|
||||
container<std::decay_t<decltype(out)>>;
|
||||
constexpr bool attr_container = container<attr_t>;
|
||||
|
||||
if constexpr (detail::is_nope_v<attr_t>)
|
||||
flags = detail::disable_attrs(flags);
|
||||
|
||||
if constexpr (
|
||||
(out_container == attr_container &&
|
||||
!was_merged_into_adjacent_container) ||
|
||||
@@ -6325,7 +6329,7 @@ namespace boost { namespace parser {
|
||||
}
|
||||
};
|
||||
|
||||
/** The literal code point parser. The produced attribute is the type of
|
||||
/** The single code point parser. The produced attribute is the type of
|
||||
the matched code point. This parser can be used to create code point
|
||||
parsers that match one or more specific code point values, by calling
|
||||
it with: a single value comparable to a code point; a set of code
|
||||
@@ -6333,7 +6337,7 @@ namespace boost { namespace parser {
|
||||
hi]`, or a set of code point values passed as a range. */
|
||||
inline constexpr parser_interface<char_parser<detail::nope>> char_;
|
||||
|
||||
/** The literal code point parser. It produces a `char32_t` attribute.
|
||||
/** The single code point parser. It produces a `char32_t` attribute.
|
||||
This parser can be used to create code point parsers that match one or
|
||||
more specific code point values, by calling it with: a single value
|
||||
comparable to a code point; a set of code point values in a string; a
|
||||
@@ -6341,7 +6345,7 @@ namespace boost { namespace parser {
|
||||
values passed as a range. */
|
||||
inline constexpr parser_interface<char_parser<detail::nope, char32_t>> cp;
|
||||
|
||||
/** The literal code unit parser. It produces a `char` attribute. This
|
||||
/** The single code unit parser. It produces a `char` attribute. This
|
||||
parser can be used to create code unit parsers that match one or more
|
||||
specific code unit values, by calling it with: a single value
|
||||
comparable to a code unit; a set of code unit values in a string; a
|
||||
@@ -6489,6 +6493,251 @@ namespace boost { namespace parser {
|
||||
return parser_interface{string_parser(str)};
|
||||
}
|
||||
|
||||
template<typename I, typename S>
|
||||
struct quoted_string_parser
|
||||
{
|
||||
constexpr quoted_string_parser() : chs_(), ch_('"') {}
|
||||
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
template<parsable_range_like R>
|
||||
#else
|
||||
template<
|
||||
typename R,
|
||||
typename Enable =
|
||||
std::enable_if_t<detail::is_parsable_range_like_v<R>>>
|
||||
#endif
|
||||
constexpr quoted_string_parser(R && r) :
|
||||
chs_(detail::make_view_begin(r), detail::make_view_end(r)), ch_(0)
|
||||
{}
|
||||
|
||||
constexpr quoted_string_parser(char32_t cp) : chs_(), ch_(cp)
|
||||
{}
|
||||
|
||||
template<
|
||||
bool UseCallbacks,
|
||||
typename Iter,
|
||||
typename Sentinel,
|
||||
typename Context,
|
||||
typename SkipParser>
|
||||
std::string call(
|
||||
std::bool_constant<UseCallbacks> use_cbs,
|
||||
Iter & first,
|
||||
Sentinel last,
|
||||
Context const & context,
|
||||
SkipParser const & skip,
|
||||
detail::flags flags,
|
||||
bool & success) const
|
||||
{
|
||||
std::string retval;
|
||||
call(use_cbs, first, last, context, skip, flags, success, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<
|
||||
bool UseCallbacks,
|
||||
typename Iter,
|
||||
typename Sentinel,
|
||||
typename Context,
|
||||
typename SkipParser,
|
||||
typename Attribute>
|
||||
void call(
|
||||
std::bool_constant<UseCallbacks> use_cbs,
|
||||
Iter & first,
|
||||
Sentinel last,
|
||||
Context const & context,
|
||||
SkipParser const & skip,
|
||||
detail::flags flags,
|
||||
bool & success,
|
||||
Attribute & retval) const
|
||||
{
|
||||
auto _ = detail::scoped_trace(
|
||||
*this, first, last, context, flags, retval);
|
||||
|
||||
if (first == last) {
|
||||
success = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto const prev_first = first;
|
||||
|
||||
auto append = [&retval,
|
||||
gen_attrs = detail::gen_attrs(flags)](auto & ctx) {
|
||||
detail::move_back(retval, _attr(ctx), gen_attrs);
|
||||
};
|
||||
|
||||
if (chs_.empty()) {
|
||||
auto const p =
|
||||
ch_ >> *((lit('\\') >> (char_('\\') | char_(ch_)) | char_) -
|
||||
ch_)[append] > ch_;
|
||||
call_impl(
|
||||
p.parser_,
|
||||
use_cbs,
|
||||
first,
|
||||
last,
|
||||
context,
|
||||
skip,
|
||||
flags,
|
||||
success,
|
||||
retval);
|
||||
} else {
|
||||
detail::remove_cv_ref_t<decltype(*first)> const ch = *first;
|
||||
bool found = false;
|
||||
if constexpr (std::is_same_v<decltype(ch), char32_t const>) {
|
||||
auto r = chs_ | detail::text::as_utf32;
|
||||
found =
|
||||
detail::text::find(r.begin(), r.end(), ch) == r.end();
|
||||
} else {
|
||||
found = detail::text::find(chs_.begin(), chs_.end(), ch) ==
|
||||
chs_.end();
|
||||
}
|
||||
if (!found) {
|
||||
success = false;
|
||||
return;
|
||||
}
|
||||
++first;
|
||||
|
||||
auto const p =
|
||||
*((lit('\\') >> (char_('\\') | char_(ch)) | char_) -
|
||||
ch)[append] > ch;
|
||||
|
||||
call_impl(
|
||||
p.parser_,
|
||||
use_cbs,
|
||||
first,
|
||||
last,
|
||||
context,
|
||||
skip,
|
||||
flags,
|
||||
success,
|
||||
retval);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
retval = Attribute();
|
||||
first = prev_first;
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
typename Parser,
|
||||
bool UseCallbacks,
|
||||
typename Iter,
|
||||
typename Sentinel,
|
||||
typename Context,
|
||||
typename SkipParser,
|
||||
typename Attribute>
|
||||
void call_impl(
|
||||
Parser & parser,
|
||||
std::bool_constant<UseCallbacks> use_cbs,
|
||||
Iter & first,
|
||||
Sentinel last,
|
||||
Context const & context,
|
||||
SkipParser const & skip,
|
||||
detail::flags flags,
|
||||
bool & success,
|
||||
Attribute & retval) const
|
||||
{
|
||||
parser.call(
|
||||
use_cbs,
|
||||
first,
|
||||
last,
|
||||
context,
|
||||
skip,
|
||||
detail::disable_skip(flags),
|
||||
success,
|
||||
retval);
|
||||
}
|
||||
|
||||
/** Returns a `parser_interface` containing a `quoted_string_parser`
|
||||
that uses `x` as its quotation marks. */
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
template<typename T>
|
||||
// clang-format off
|
||||
requires (!parsable_range_like<T>)
|
||||
#else
|
||||
template<
|
||||
typename T,
|
||||
typename Enable =
|
||||
std::enable_if_t<!detail::is_parsable_range_like_v<T>>>
|
||||
#endif
|
||||
constexpr auto operator()(T x) const noexcept
|
||||
// clang-format on
|
||||
{
|
||||
BOOST_PARSER_ASSERT(
|
||||
(chs_.empty() && ch_ == '"' &&
|
||||
"If you're seeing this, you tried to chain calls on "
|
||||
"quoted_string, like 'quoted_string('\"')('\\'')'. Quit "
|
||||
"it!'"));
|
||||
return parser_interface(quoted_string_parser(std::move(x)));
|
||||
}
|
||||
|
||||
/** Returns a `parser_interface` containing a `quoted_string_parser`
|
||||
that accepts any of the values in `r` as its quotation marks. If
|
||||
the input being matched during the parse is a a sequence of
|
||||
`char32_t`, the elements of `r` are transcoded from their presumed
|
||||
encoding to UTF-32 during the comparison. Otherwise, the
|
||||
character begin matched is directly compared to the elements of
|
||||
`r`. */
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
template<parsable_range_like R>
|
||||
#else
|
||||
template<
|
||||
typename R,
|
||||
typename Enable =
|
||||
std::enable_if_t<detail::is_parsable_range_like_v<R>>>
|
||||
#endif
|
||||
constexpr auto operator()(R && r) const noexcept
|
||||
{
|
||||
BOOST_PARSER_ASSERT(((
|
||||
!std::is_rvalue_reference_v<R &&> ||
|
||||
!detail::is_range<detail::remove_cv_ref_t<
|
||||
R>>)&&"It looks like you tried to pass an rvalue range to "
|
||||
"quoted_string(). Don't do that, or you'll end up "
|
||||
"with dangling references."));
|
||||
BOOST_PARSER_ASSERT(
|
||||
(chs_.empty() && ch_ == '"' &&
|
||||
"If you're seeing this, you tried to chain calls on "
|
||||
"quoted_string, like 'quoted_string(char-range)(char-range)'. "
|
||||
" Quit it!'"));
|
||||
return parser_interface(
|
||||
quoted_string_parser<
|
||||
decltype(detail::make_view_begin(r)),
|
||||
decltype(detail::make_view_end(r))>((R &&) r));
|
||||
}
|
||||
|
||||
BOOST_PARSER_SUBRANGE<I, S> chs_;
|
||||
char32_t ch_;
|
||||
};
|
||||
|
||||
/** Parses a string delimited by quotation marks. This parser can be used
|
||||
to create parsers that accept one or more specific quotation mark
|
||||
characters. By default, the quotation marks are `'"'`; an alternate
|
||||
quotation mark can be specified by calling this parser with a single
|
||||
character, or a range of characters. If a range is specified, the
|
||||
opening quote must be one of the characters specified, and the closing
|
||||
quote must match the opening quote. Quotation marks may appear within
|
||||
the string if escaped with a backslash, and a pair of backslashes is
|
||||
treated as a single escaped backslash; all other backslashes cuase the
|
||||
parse to fail. Skipping is disabled during parsing of the entire
|
||||
quoted string, including the quotation marks. There is an expectation
|
||||
point before the closing qutation mark. Produces a `std::string`
|
||||
attribute. */
|
||||
inline constexpr parser_interface<
|
||||
quoted_string_parser<char const *, char const *>>
|
||||
quoted_string;
|
||||
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
template<parsable_range_like R>
|
||||
#else
|
||||
template<typename R>
|
||||
#endif
|
||||
quoted_string_parser(R r) -> quoted_string_parser<
|
||||
decltype(detail::make_view_begin(r)),
|
||||
decltype(detail::make_view_end(r))>;
|
||||
|
||||
quoted_string_parser(char32_t)
|
||||
-> quoted_string_parser<char const *, char const *>;
|
||||
|
||||
/** Returns a parser that matches `str` that produces no attribute. */
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
template<parsable_range_like R>
|
||||
|
||||
@@ -364,12 +364,17 @@ namespace boost { namespace parser {
|
||||
character being matched. */
|
||||
struct digit_parser;
|
||||
|
||||
/** Maches a particular string, delimited by an iterator sentinel pair;
|
||||
/** Matches a particular string, delimited by an iterator sentinel pair;
|
||||
produces no attribute. */
|
||||
template<typename StrIter, typename StrSentinel>
|
||||
struct string_parser;
|
||||
|
||||
/** Maches an end-of-line (`NewlinesOnly == true`), whitespace
|
||||
/** Matches a string delimited by quotation marks; produces a
|
||||
`std::string` attribute. */
|
||||
template<typename I, typename S>
|
||||
struct quoted_string_parser;
|
||||
|
||||
/** Matches an end-of-line (`NewlinesOnly == true`), whitespace
|
||||
(`NewlinesOnly == false`), or (`NoNewlines == true`) blank (whitespace
|
||||
but not newline) code point, based on the Unicode definitions of each
|
||||
(also matches the two code points `"\r\n"`). Produces no
|
||||
@@ -377,7 +382,7 @@ namespace boost { namespace parser {
|
||||
template<bool NewlinesOnly, bool NoNewlines>
|
||||
struct ws_parser;
|
||||
|
||||
/** Maches the strings "true" and "false", producing an attribute of
|
||||
/** Matches the strings "true" and "false", producing an attribute of
|
||||
`true` or `false`, respectively, and fails on any other input. */
|
||||
struct bool_parser;
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ macro(add_test_executable name)
|
||||
add_test(NAME ${name} COMMAND ${name} --gtest_catch_exceptions=1)
|
||||
endmacro()
|
||||
|
||||
#add_test_executable(quoted_string)
|
||||
add_test_executable(all_t)
|
||||
add_test_executable(search)
|
||||
add_test_executable(split)
|
||||
@@ -67,3 +68,4 @@ add_test_executable(class_type)
|
||||
add_test_executable(case_fold_generated)
|
||||
add_test_executable(no_case)
|
||||
add_test_executable(merge_separate)
|
||||
add_test_executable(parse_coords_new)
|
||||
|
||||
194
test/parse_coords_new.cpp
Normal file
194
test/parse_coords_new.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Copyright (C) 2024 Phil Endecott
|
||||
* Copyright (C) 2024 T. Zachary Laine
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
|
||||
#include "boost/parser/parser.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace bp = boost::parser;
|
||||
|
||||
using namespace boost::hana::literals;
|
||||
|
||||
namespace g2d {
|
||||
struct Vector
|
||||
{
|
||||
double x, y;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, Vector vec)
|
||||
{
|
||||
os << "vec{" << vec.x << ", " << vec.y << "}";
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bp::rule<struct uint_0_60, unsigned int> uint_0_60 = "uint_0_60";
|
||||
bp::rule<struct double_0_60, double> double_0_60 = "double_0_60";
|
||||
bp::rule<struct degrees_decimal_minutes, double> degrees_decimal_minutes = "degrees_decimal_minutes";
|
||||
bp::rule<struct degrees_minutes_seconds, double> degrees_minutes_seconds = "degrees_minutes_seconds";
|
||||
bp::rule<struct degrees, double> degrees = "degrees";
|
||||
bp::rule<struct latitude, double> latitude = "latitude";
|
||||
bp::rule<struct longitude, double> longitude = "longitude";
|
||||
bp::rule<struct signed_latitude, double> signed_latitude = "signed_latitude";
|
||||
bp::rule<struct signed_longitude, double> signed_longitude = "signed_longitude";
|
||||
bp::rule<struct latlon, g2d::Vector> latlon = "latlon";
|
||||
|
||||
|
||||
const auto degrees_symbol = bp::no_case[ bp::lit("degrees") | bp::lit("deg") | bp::lit('d') ];
|
||||
const auto minutes_symbol = bp::no_case[ bp::lit('\'') | bp::lit("minutes") | bp::lit("min") | bp::lit('m') ];
|
||||
const auto seconds_symbol = bp::no_case[ bp::lit('"') | bp::lit("seconds") | bp::lit("sec") | bp::lit('s') ];
|
||||
|
||||
const auto uint_0_60_def = bp::uint_ [( [](auto ctx) { _pass(ctx) = _attr(ctx) < 60U; _val(ctx) = _attr(ctx); } )];
|
||||
const auto double_0_60_def = bp::double_ [( [](auto ctx) { _pass(ctx) = _attr(ctx) < 60; _val(ctx) = _attr(ctx); } )];
|
||||
|
||||
const auto decimal_degrees = bp::double_ >> -degrees_symbol;
|
||||
|
||||
const auto degrees_decimal_minutes_def = (bp::uint_ >> -degrees_symbol
|
||||
>> (double_0_60 - '.') >> -minutes_symbol) [( [](auto ctx) {
|
||||
auto d = _attr(ctx)[0_c];
|
||||
auto m = _attr(ctx)[1_c];
|
||||
_val(ctx) = d + m/60.0;
|
||||
} )];
|
||||
|
||||
const auto degrees_minutes_seconds_def = (bp::uint_ >> -degrees_symbol
|
||||
>> uint_0_60 >> -minutes_symbol
|
||||
>> (double_0_60 - '.') >> -seconds_symbol) [( [](auto ctx) {
|
||||
auto d = _attr(ctx)[0_c];
|
||||
auto m = _attr(ctx)[1_c];
|
||||
auto s = _attr(ctx)[2_c];
|
||||
_val(ctx) = d + m/60.0 + s/3600.0;
|
||||
} )];
|
||||
|
||||
const auto degrees_def = degrees_minutes_seconds
|
||||
| degrees_decimal_minutes
|
||||
| decimal_degrees;
|
||||
|
||||
const auto northsouth = bp::no_case[ bp::char_("ns") ];
|
||||
const auto eastwest = bp::no_case[ bp::char_("ew") ];
|
||||
|
||||
const auto latitude_def = (degrees >> northsouth) [( [](auto ctx) {
|
||||
auto d = _attr(ctx)[0_c];
|
||||
auto ns = _attr(ctx)[1_c];
|
||||
_pass(ctx) = d <= 90;
|
||||
_val(ctx) = ns=='S' || ns=='s' ? -d : d;
|
||||
} )];
|
||||
|
||||
const auto longitude_def = (degrees >> eastwest) [( [](auto ctx) {
|
||||
auto d = _attr(ctx)[0_c];
|
||||
auto ew = _attr(ctx)[1_c];
|
||||
_pass(ctx) = d <= 180;
|
||||
_val(ctx) = ew=='W' || ew=='w' ? -d : d;
|
||||
} )];
|
||||
|
||||
const auto signed_degrees = bp::double_ >> -degrees_symbol;
|
||||
|
||||
const auto signed_latitude_def = signed_degrees [( [](auto ctx) { auto d = _attr(ctx); _pass(ctx) = -90 <= d && d <= 90; _val(ctx) = _attr(ctx); } )];
|
||||
const auto signed_longitude_def = signed_degrees [( [](auto ctx) { auto d = _attr(ctx); _pass(ctx) = -180 <= d && d <= 180; _val(ctx) = _attr(ctx); } )];
|
||||
|
||||
const auto latlon_def = ((latitude >> longitude) [( [](auto ctx) { _val(ctx) = g2d::Vector{_attr(ctx)[1_c], _attr(ctx)[0_c]}; } )] )
|
||||
| ((longitude >> latitude) [( [](auto ctx) { _val(ctx) = g2d::Vector{_attr(ctx)[0_c], _attr(ctx)[1_c]}; } )] )
|
||||
| ((signed_longitude >> signed_latitude)
|
||||
[( [](auto ctx) { _val(ctx) = g2d::Vector{_attr(ctx)[0_c], _attr(ctx)[1_c]}; } )] );
|
||||
|
||||
BOOST_PARSER_DEFINE_RULES(uint_0_60);
|
||||
BOOST_PARSER_DEFINE_RULES(double_0_60);
|
||||
BOOST_PARSER_DEFINE_RULES(degrees_decimal_minutes);
|
||||
BOOST_PARSER_DEFINE_RULES(degrees_minutes_seconds);
|
||||
BOOST_PARSER_DEFINE_RULES(degrees);
|
||||
BOOST_PARSER_DEFINE_RULES(latitude);
|
||||
BOOST_PARSER_DEFINE_RULES(longitude);
|
||||
BOOST_PARSER_DEFINE_RULES(signed_latitude);
|
||||
BOOST_PARSER_DEFINE_RULES(signed_longitude);
|
||||
BOOST_PARSER_DEFINE_RULES(latlon);
|
||||
|
||||
|
||||
#if 0
|
||||
static std::optional<g2d::Vector> try_parse_latlon(std::string_view s)
|
||||
{
|
||||
auto input = latlon >> bp::eoi;
|
||||
return parse(s,input, bp::ws|bp::lit(','));
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
assert(argc==2);
|
||||
|
||||
auto opt_coords = try_parse_latlon(argv[1]);
|
||||
|
||||
if (!opt_coords) {
|
||||
std::cerr << "parse error\n";
|
||||
return 1;
|
||||
} else {
|
||||
std::cout << opt_coords->x << " " << opt_coords->y << "\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(parse_coords, all_examples)
|
||||
{
|
||||
std::vector<std::string> test_coords = {
|
||||
"12.34 N, 56.78 E",
|
||||
"56.78,-12.34",
|
||||
"12d 30' n 45d 15' 7\" w",
|
||||
"12 30 45 N, 45 15 7 W",
|
||||
"12d 30m 15s S 50d 59m 59s W",
|
||||
"50d 0.5m n 50d 59m 59s W"};
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[0], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - 56.78), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - 12.34), 0.001);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[1], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - 56.78), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - -12.34), 0.001);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[2], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - -45.2519), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - 12.5), 0.001);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[3], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - -45.2519), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - 12.5125), 0.001);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[4], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - -50.9997), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - -12.5042), 0.001);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[5], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - -50.9997), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - 50.0083), 0.001);
|
||||
}
|
||||
}
|
||||
@@ -2167,6 +2167,13 @@ TEST(parser, combined_seq_and_or)
|
||||
EXPECT_EQ(chars, "xyz");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
constexpr auto parser = int_ >> -(lit('a') | 'b');
|
||||
auto result = parse("34b", parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 34);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parser, eol_)
|
||||
|
||||
127
test/quoted_string.cpp
Normal file
127
test/quoted_string.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (C) 2024 T. Zachary Laine
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/parser/parser.hpp>
|
||||
#include <boost/parser/transcode_view.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace bp = boost::parser;
|
||||
|
||||
TEST(quoted_string, basic)
|
||||
{
|
||||
constexpr auto parser = bp::quoted_string;
|
||||
|
||||
{
|
||||
auto result = bp::parse("", parser, bp::ws);
|
||||
EXPECT_FALSE(result);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(R"("foo")", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo");
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(R"("foo\\")", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo\\");
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(R"("\"foo\"")", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "\"foo\"");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(quoted_string, different_char)
|
||||
{
|
||||
constexpr auto parser = bp::quoted_string('\'');
|
||||
|
||||
{
|
||||
auto result = bp::parse("", parser, bp::ws);
|
||||
EXPECT_FALSE(result);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(R"('foo')", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo");
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(R"('foo\\')", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo\\");
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(R"('\'foo\'')", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "'foo'");
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
TEST(quoted_string, char_set)
|
||||
{
|
||||
constexpr auto parser = bp::quoted_string("'\"");
|
||||
|
||||
{
|
||||
auto result = bp::parse("", parser, bp::ws);
|
||||
EXPECT_FALSE(result);
|
||||
}
|
||||
|
||||
{
|
||||
EXPECT_FALSE(bp::parse(R"('foo")", parser, bp::ws));
|
||||
EXPECT_FALSE(bp::parse(R"("foo')", parser, bp::ws));
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(R"('foo')", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo");
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(R"("foo")", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo");
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(R"('foo\\')", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo\\");
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(R"("foo\\")", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo\\");
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(R"('\'foo\'')", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "'foo'");
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(R"("\"foo\"")", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "'foo'");
|
||||
}
|
||||
|
||||
{
|
||||
// Can't escape arbitrary characters, only backslash and the quote
|
||||
// character.
|
||||
EXPECT_FALSE(bp::parse(R"("\'foo")", parser, bp::ws));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -437,17 +437,35 @@ TEST(replace, join_compat)
|
||||
|
||||
TEST(replace, doc_examples)
|
||||
{
|
||||
// clang-format off
|
||||
{
|
||||
auto rng = "XYZaaXYZbaabaXYZXYZ" | bp::replace(bp::lit("XYZ"), "foo");
|
||||
namespace bp = boost::parser;
|
||||
auto card_number = bp::int_ >> bp::repeat(3)['-' >> bp::int_];
|
||||
auto rng = "My credit card number is 1234-5678-9012-3456." | bp::replace(card_number, "XXXX-XXXX-XXXX-XXXX");
|
||||
int count = 0;
|
||||
// Prints foo aa foo baaba foo foo.
|
||||
// Prints My credit card number is XXXX-XXXX-XXXX-XXXX.
|
||||
for (auto subrange : rng) {
|
||||
std::cout << std::string_view(subrange.begin(), subrange.end() - subrange.begin()) << " ";
|
||||
std::cout << std::string_view(subrange.begin(), subrange.end() - subrange.begin());
|
||||
++count;
|
||||
}
|
||||
std::cout << "\n";
|
||||
assert(count == 6);
|
||||
assert(count == 3);
|
||||
}
|
||||
#if BOOST_PARSER_USE_CONCEPTS && (!defined(__GNUC__) || 12 <= __GNUC__)
|
||||
{
|
||||
namespace bp = boost::parser;
|
||||
auto card_number = bp::int_ >> bp::repeat(3)['-' >> bp::int_];
|
||||
auto rng = "My credit card number is 1234-5678-9012-3456." |
|
||||
bp::replace(card_number, "XXXX-XXXX-XXXX-XXXX") |
|
||||
std::views::join;
|
||||
std::string replace_result;
|
||||
for (auto ch : rng) {
|
||||
replace_result.push_back(ch);
|
||||
}
|
||||
assert(replace_result == "My credit card number is XXXX-XXXX-XXXX-XXXX.");
|
||||
#endif
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -297,6 +297,15 @@ int main()
|
||||
|
||||
PARSE(string("h"));
|
||||
|
||||
std::cout << "\n\n"
|
||||
<< "----------------------------------------\n"
|
||||
<< "| quoted_string() |\n"
|
||||
<< "----------------------------------------\n";
|
||||
|
||||
PARSE(quoted_string);
|
||||
PARSE(quoted_string('\''));
|
||||
PARSE(quoted_string("'\""));
|
||||
|
||||
std::cout << "\n\n"
|
||||
<< "----------------------------------------\n"
|
||||
<< "| eol |\n"
|
||||
|
||||
Reference in New Issue
Block a user