使用转义序列处理为带引号的字符串创建 boost::spirit::x3 解析器
Creating a boost::spirit::x3 parser for quoted strings with escape sequence handling
我需要为我的自定义语言的带引号的字符串创建一个解析器,该解析器还将正确处理转义序列,其中包括允许在字符串中转义引号。这是我当前的字符串解析器:
x3::lexeme[quote > *(x3::char_ - quote) > quote]
其中 quote
只是 '"'
的常量表达式。它不进行任何转义序列处理。我知道 boost::spirit::classic::lex_escape_ch_p
,但我不知道如何将其与 boost::spirit::x3
工具(或一般)一起使用。我如何创建一个执行此操作的解析器?解析器必须识别大多数转义序列,例如 '\n'
、'\t'
等常见转义序列,以及十六进制、八进制和 ansi 转义序列等更复杂的转义序列。
如果此 post 有问题,我深表歉意,这是我第一次 post 这样做。
编辑:
下面是我最终实现解析器的方式:
x3::lexeme[quote > *(
("\\"" >> &x3::char_) >> x3::attr(quote) | ~x3::char_(quote)
) > quote]
[handle_escape_sequences];
其中 handle_escape_sequences
是一个 lambda:
auto handle_escape_sequences = [&](auto&& context) -> void {
std::string& str = x3::_val(context);
uint32_t i{};
static auto replace = [&](const char replacement) -> void {
str[i++] = replacement;
};
if (!classic::parse(std::begin(str), std::end(str), *classic::lex_escape_ch_p[replace]).full)
throw Error{ "invalid literal" }; // invalid escape sequence most likely
str.resize(i);
};
它进行完整的 ANSI 转义序列解析,这意味着您可以使用它进行各种奇特的终端操作,例如设置文本颜色、光标位置等。
这是规则的完整定义以及它所依赖的所有内容(我只是从我的代码中挑选了所有与它相关的东西,所以这就是为什么结果看起来像意大利面条一样)以防有人碰巧需要它:
#include <boost\spirit\home\x3.hpp>
#include <boost\spirit\include\classic_utility.hpp>
using namespace boost::spirit;
#define RULE_DECLARATION(rule_name, attribute_type) \
inline namespace Tag { class rule_name ## _tag; } \
x3::rule<Tag::rule_name ## _tag, attribute_type, true> rule_name = #rule_name; \
#define SIMPLE_RULE_DEFINITION(rule_name, attribute_type, definition) \
RULE_DECLARATION(rule_name, attribute_type) \
auto rule_name ## _def = definition; \
BOOST_SPIRIT_DEFINE(rule_name);
constexpr char quote = '"';
template <class Base, class>
struct Access_base_s : Base {
using Base::Base, Base::operator=;
};
template <class Base, class Tag>
using Unique_alias_for = Access_base_s<Base, Tag>;
using String_literal = Unique_alias_for<std::string, class String_literal_tag>;
SIMPLE_RULE_DEFINITION(string_literal, String_literal,
x3::lexeme[quote > *(
("\\"" >> &x3::char_) >> x3::attr(quote) | ~x3::char_(quote)
) > quote]
[handle_escape_sequences];
);
我在这个网站上有很多这样的例子¹
让 met 从简化表达式开始(~charset
可能比 charset - exceptions
更有效):
x3::lexeme['"' > *~x3::char_('"')) > '"']
现在,为了允许转义,我们可以对它们进行临时解码:
auto qstring = x3::lexeme['"' > *(
"\n" >> x3::attr('\n')
| "\b" >> x3::attr('\b')
| "\f" >> x3::attr('\f')
| "\t" >> x3::attr('\t')
| "\v" >> x3::attr('\v')
| "\0" >> x3::attr('[=11=]')
| "\r" >> x3::attr('\r')
| "\n" >> x3::attr('\n')
| "\" >> x3::char_("\"\")
| ~x3::char_('"')
) > '"'];
或者,您可以使用符号方法,包括或不包括斜杠:
x3::symbols<char> escapes;
escapes.add
( "\n", '\n')
( "\b", '\b')
( "\f", '\f')
( "\t", '\t')
( "\v", '\v')
( "\0", '[=12=]')
( "\r", '\r')
( "\n", '\n')
( "\\", '\')
( "\\"", '"');
auto qstring = x3::lexeme['"' > *(escapes | ~x3::char_('"')) > '"'];
也看到了Live On Coliru
我想我更喜欢手卷的树枝,因为它们可以让你灵活地做,例如he/octal 转义(注意与 [=18=]
的冲突):
| "\" >> x3::int_parser<char, 8, 1, 3>()
| "\x" >> x3::int_parser<char, 16, 2, 2>()
这也很好用:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>
int main() {
namespace x3 = boost::spirit::x3;
auto qstring = x3::lexeme['"' > *(
"\n" >> x3::attr('\n')
| "\b" >> x3::attr('\b')
| "\f" >> x3::attr('\f')
| "\t" >> x3::attr('\t')
| "\v" >> x3::attr('\v')
| "\r" >> x3::attr('\r')
| "\n" >> x3::attr('\n')
| "\" >> x3::char_("\"\")
| "\" >> x3::int_parser<char, 8, 1, 3>()
| "\x" >> x3::int_parser<char, 16, 2, 2>()
| ~x3::char_('"')
) > '"'];
for (std::string const input : { R"("\ttest\x41\x42\x43 \x031\x032\x033 \"hello\"\r\n")" }) {
std::string output;
auto f = begin(input), l = end(input);
if (x3::phrase_parse(f, l, qstring, x3::blank, output)) {
std::cout << "[" << output << "]\n";
} else {
std::cout << "Failed\n";
}
if (f != l) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(f,l)) << "\n";
}
}
}
版画
[ testABC 123 "hello"
]
¹ 看看这些
- 齐,简单:
- 齐,完整JSON式:Handling utf-8 in Boost.Spirit with utf-32 parser
- 齐,实用 X500/LDAP 尊贵的名字风格:
- 齐,实用的C风格转义
我需要为我的自定义语言的带引号的字符串创建一个解析器,该解析器还将正确处理转义序列,其中包括允许在字符串中转义引号。这是我当前的字符串解析器:
x3::lexeme[quote > *(x3::char_ - quote) > quote]
其中 quote
只是 '"'
的常量表达式。它不进行任何转义序列处理。我知道 boost::spirit::classic::lex_escape_ch_p
,但我不知道如何将其与 boost::spirit::x3
工具(或一般)一起使用。我如何创建一个执行此操作的解析器?解析器必须识别大多数转义序列,例如 '\n'
、'\t'
等常见转义序列,以及十六进制、八进制和 ansi 转义序列等更复杂的转义序列。
如果此 post 有问题,我深表歉意,这是我第一次 post 这样做。
编辑:
下面是我最终实现解析器的方式:
x3::lexeme[quote > *(
("\\"" >> &x3::char_) >> x3::attr(quote) | ~x3::char_(quote)
) > quote]
[handle_escape_sequences];
其中 handle_escape_sequences
是一个 lambda:
auto handle_escape_sequences = [&](auto&& context) -> void {
std::string& str = x3::_val(context);
uint32_t i{};
static auto replace = [&](const char replacement) -> void {
str[i++] = replacement;
};
if (!classic::parse(std::begin(str), std::end(str), *classic::lex_escape_ch_p[replace]).full)
throw Error{ "invalid literal" }; // invalid escape sequence most likely
str.resize(i);
};
它进行完整的 ANSI 转义序列解析,这意味着您可以使用它进行各种奇特的终端操作,例如设置文本颜色、光标位置等。
这是规则的完整定义以及它所依赖的所有内容(我只是从我的代码中挑选了所有与它相关的东西,所以这就是为什么结果看起来像意大利面条一样)以防有人碰巧需要它:
#include <boost\spirit\home\x3.hpp>
#include <boost\spirit\include\classic_utility.hpp>
using namespace boost::spirit;
#define RULE_DECLARATION(rule_name, attribute_type) \
inline namespace Tag { class rule_name ## _tag; } \
x3::rule<Tag::rule_name ## _tag, attribute_type, true> rule_name = #rule_name; \
#define SIMPLE_RULE_DEFINITION(rule_name, attribute_type, definition) \
RULE_DECLARATION(rule_name, attribute_type) \
auto rule_name ## _def = definition; \
BOOST_SPIRIT_DEFINE(rule_name);
constexpr char quote = '"';
template <class Base, class>
struct Access_base_s : Base {
using Base::Base, Base::operator=;
};
template <class Base, class Tag>
using Unique_alias_for = Access_base_s<Base, Tag>;
using String_literal = Unique_alias_for<std::string, class String_literal_tag>;
SIMPLE_RULE_DEFINITION(string_literal, String_literal,
x3::lexeme[quote > *(
("\\"" >> &x3::char_) >> x3::attr(quote) | ~x3::char_(quote)
) > quote]
[handle_escape_sequences];
);
我在这个网站上有很多这样的例子¹
让 met 从简化表达式开始(~charset
可能比 charset - exceptions
更有效):
x3::lexeme['"' > *~x3::char_('"')) > '"']
现在,为了允许转义,我们可以对它们进行临时解码:
auto qstring = x3::lexeme['"' > *(
"\n" >> x3::attr('\n')
| "\b" >> x3::attr('\b')
| "\f" >> x3::attr('\f')
| "\t" >> x3::attr('\t')
| "\v" >> x3::attr('\v')
| "\0" >> x3::attr('[=11=]')
| "\r" >> x3::attr('\r')
| "\n" >> x3::attr('\n')
| "\" >> x3::char_("\"\")
| ~x3::char_('"')
) > '"'];
或者,您可以使用符号方法,包括或不包括斜杠:
x3::symbols<char> escapes;
escapes.add
( "\n", '\n')
( "\b", '\b')
( "\f", '\f')
( "\t", '\t')
( "\v", '\v')
( "\0", '[=12=]')
( "\r", '\r')
( "\n", '\n')
( "\\", '\')
( "\\"", '"');
auto qstring = x3::lexeme['"' > *(escapes | ~x3::char_('"')) > '"'];
也看到了Live On Coliru
我想我更喜欢手卷的树枝,因为它们可以让你灵活地做,例如he/octal 转义(注意与 [=18=]
的冲突):
| "\" >> x3::int_parser<char, 8, 1, 3>()
| "\x" >> x3::int_parser<char, 16, 2, 2>()
这也很好用:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>
int main() {
namespace x3 = boost::spirit::x3;
auto qstring = x3::lexeme['"' > *(
"\n" >> x3::attr('\n')
| "\b" >> x3::attr('\b')
| "\f" >> x3::attr('\f')
| "\t" >> x3::attr('\t')
| "\v" >> x3::attr('\v')
| "\r" >> x3::attr('\r')
| "\n" >> x3::attr('\n')
| "\" >> x3::char_("\"\")
| "\" >> x3::int_parser<char, 8, 1, 3>()
| "\x" >> x3::int_parser<char, 16, 2, 2>()
| ~x3::char_('"')
) > '"'];
for (std::string const input : { R"("\ttest\x41\x42\x43 \x031\x032\x033 \"hello\"\r\n")" }) {
std::string output;
auto f = begin(input), l = end(input);
if (x3::phrase_parse(f, l, qstring, x3::blank, output)) {
std::cout << "[" << output << "]\n";
} else {
std::cout << "Failed\n";
}
if (f != l) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(f,l)) << "\n";
}
}
}
版画
[ testABC 123 "hello"
]
¹ 看看这些
- 齐,简单:
- 齐,完整JSON式:Handling utf-8 in Boost.Spirit with utf-32 parser
- 齐,实用 X500/LDAP 尊贵的名字风格:
- 齐,实用的C风格转义