mirror of
https://github.com/boostorg/parser.git
synced 2026-01-19 04:22:13 +00:00
WIP_quoted_string
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -6329,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
|
||||
@@ -6337,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
|
||||
@@ -6345,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
|
||||
@@ -6493,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)
|
||||
|
||||
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
|
||||
@@ -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