为了熟悉它,我正在用精神X3编写一个解析器,尽管我非常熟悉气,但在X3中仍然遇到了一些障碍。
例如,齐示例包括一个基本的XML解析器,您应该使用凤凰占位符来匹配以前匹配的值。然而,我只能在X3中找到答案:
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace x3 = boost::spirit::x3;
namespace mytest
{
struct SimpleElement
{
std::string tag;
std::string content;
};
} // namespace bbspirit
BOOST_FUSION_ADAPT_STRUCT
(
mytest::SimpleElement, tag, content
)
namespace mytest
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using x3::lit;
using x3::lexeme;
using ascii::char_;
const x3::rule<class SimpleElementID, SimpleElement> simpleTag = "simpleTag";
auto assignTag = [](auto& ctx)
{
x3::_val(ctx).tag = x3::_attr(ctx);
};
auto testTag = [](auto& ctx)
{
x3::_pass(ctx) =
(x3::_val(ctx).tag == x3::_attr(ctx));
};
auto assignContent = [](auto& ctx)
{
x3::_val(ctx).content = x3::_attr(ctx);
};
auto const simpleTag_def
= '['
>> x3::lexeme[+(char_ - ']')][assignTag]
>> ']'
>> x3::lexeme[
+(char_ - x3::lit("[/"))]
[assignContent]
>> "[/"
>> x3::lexeme[+(char_ - ']')][testTag]
>> ']'
;
BOOST_SPIRIT_DEFINE(simpleTag);
} // namespace bbspirit
int main()
{
const std::string text = "[test]Hello World![/test]";
std::string::const_iterator start = std::begin(text);
const std::string::const_iterator stop = std::end(text);
mytest::SimpleElement element{};
bool result =
phrase_parse(start, stop, mytest::simpleTag, x3::ascii::space, element);
if (!result)
{
std::cout << "failed to parse!\n";
}
else
{
std::cout << "tag : " << element.tag << '\n';
std::cout << "content: " << element.content << '\n';
}
}(链接:https://wandbox.org/permlink/xLZN9plcOwkSKCrD )
但是,如果我试图解析类似于[test]Hello [/World[/test]的内容,它就不能工作,因为我没有在这里指定正确的遗漏:
>> x3::lexeme[
+(char_ - x3::lit("[/"))]
[assignContent]本质上,我想告诉解析器如下所示:
>> x3::lexeme[
+(char_ - (x3::lit("[/") << *the start tag* << ']') )]
[assignContent]我怎么能继续这么做?另外,在X3中引用开始标记并随后匹配它的方式是“最好”的方式,还是有更好/更首选的方法?
谢谢!
发布于 2020-06-02 15:11:48
问得好。
最好的解决办法是完全按照XML所做的做:在标记数据中取缔[/。事实上,XML禁止使用< (因为它可能打开一个嵌套标记,并且您不希望为了查找它是否是一个有效的子标记而可能先读取整个流)。
XML使用字符实体(“转义”,如
<和>)或未解析的字符数据(CDATA[])来编码需要这些字符的内容。
接下来,您当然可以使用!closeTag属性成员(如您已经做过的)做一个负的前瞻性断言(-closeTag或tag )。
把规则重新拼成一小部分,也没那么糟
注意,我消除了使用
, true>模板参数对simpleTag规则手动传播标记/内容的需要。请参阅促进精神:“语义行为是邪恶的”?
const x3::rule<class SimpleElementID, SimpleElement, true> simpleTag = "simpleTag";
auto testTag = [](auto& ctx) { _pass(ctx) = (_val(ctx).tag == _attr(ctx)); };
auto openTag = '[' >> x3::lexeme[+(char_ - ']')] >> ']';
auto closeTag = "[/" >> x3::lexeme[+(char_ - ']')] [testTag] >> ']';
auto tagContents = x3::lexeme[ +(char_ - closeTag) ];
auto const simpleTag_def
= openTag
>> tagContents
>> x3::omit [ closeTag ]
;看吧,住在Coliru
背景
这是可行的,但最终变得相当笨拙,因为它意味着使用周围的语义操作,并且也违背了属性引用的自然绑定。
从盒子里想出一点:
在齐中,您可以使用qi::locals或继承属性 (参见docs中的一个非常类似的示例:MiniXML)。
这两种方法都会产生使用您的信息扩展解析器上下文的净效果。
X3没有这样的“高级”特性。但是它确实有扩展上下文的构建块:x3::witt<>(data) [ p ]。
X3::与
在这个简单的例子中,这看起来有些过火了,但在某些时候,您将欣赏如何在规则中使用额外的上下文,而不必将属性类型作为人质:
struct TagName{};
auto openTag
= x3::rule<struct openTagID, std::string, true> {"openTag"}
= ('[' >> x3::lexeme[+(char_ - ']')] >> ']')
[([](auto& ctx) { x3::get<TagName>(ctx) = _attr(ctx); })]
;
auto closeTag
= x3::rule<struct closeTagID, std::string, true> {"closeTag"}
= ("[/" >> x3::lexeme[+(char_ - ']')] >> ']')
[([](auto& ctx) { _pass(ctx) = (x3::get<TagName>(ctx) == _attr(ctx)); })]
;
auto tagContents
= x3::rule<struct openTagID, std::string> {"tagContents"}
= x3::lexeme[ +(char_ - closeTag) ];
auto const simpleTag
= x3::rule<class SimpleElementID, SimpleElement, true> {"simpleTag"}
= x3::with<TagName>(std::string()) [
openTag
>> tagContents
>> x3::omit [ closeTag ]
];看吧,住在Coliru
发布于 2020-06-03 17:26:58
我不想尝试用字符串和匹配来构建一艘船,而是建议制作一个适合于这项工作的工具。
#include <boost/spirit/home/x3.hpp>
namespace x3e
{
struct confix_tag {};
namespace x3 = boost::spirit::x3;
template <typename Parser, typename Iterator,
typename Context, typename RContext>
inline Iterator seek(Parser const& p, Iterator& iter, Iterator const& last,
Context const& context, RContext& rcontext)
{
Iterator start = iter;
for (;; iter = ++start)
if (p.parse(iter, last, context, rcontext, x3::unused))
return start;
return last;
}
template <typename Prefix, typename Subject, typename Postfix>
struct confix_directive : x3::unary_parser<Subject, confix_directive<Prefix, Subject, Postfix>>
{
typedef x3::unary_parser<Subject, confix_directive<Prefix, Subject, Postfix>> base_type;
static bool const is_pass_through_unary = true;
constexpr confix_directive(Prefix const& prefix, Subject const& subject, Postfix const& postfix)
: base_type(subject),
prefix(prefix),
postfix(postfix)
{
}
template <typename Iterator,
typename Context, typename RContext, typename Attribute>
bool parse(Iterator& first, Iterator const& last,
Context const& context, RContext& rcontext, Attribute& attr) const
{
auto& confix_val = boost::fusion::at_c<0>(attr);
Iterator iter = first;
if (!prefix.parse(iter, last, context, rcontext, confix_val))
return false;
Iterator postfix_iter = iter;
do {
Iterator postfix_start = x3e::seek(postfix, postfix_iter, last, x3::make_context<confix_tag>(confix_val, context), rcontext);
if (postfix_start == last)
return false;
if (this->subject.parse(iter, postfix_start, context, rcontext, boost::fusion::at_c<1>(attr))) {
first = postfix_iter;
return true;
}
} while (postfix_iter != last);
return false;
}
Prefix prefix;
Postfix postfix;
};
template<typename Prefix, typename Postfix>
struct confix_gen
{
template<typename Subject>
constexpr confix_directive<
Prefix, typename x3::extension::as_parser<Subject>::value_type, Postfix>
operator[](Subject const& subject) const
{
return { prefix, as_parser(subject), postfix };
}
Prefix prefix;
Postfix postfix;
};
template <typename Prefix, typename Postfix>
constexpr confix_gen<typename x3::extension::as_parser<Prefix>::value_type,
typename x3::extension::as_parser<Postfix>::value_type>
confix(Prefix const& prefix, Postfix const& postfix)
{
return { as_parser(prefix), as_parser(postfix) };
}
struct confix_value_matcher : x3::parser<confix_value_matcher>
{
typedef x3::unused_type attribute_type;
static bool const has_attribute = false;
template <typename Iterator, typename Context, typename RContext>
static bool parse(Iterator& iter, Iterator const& last,
Context const& context, RContext&, x3::unused_type)
{
x3::skip_over(iter, last, context);
for (auto const& e : x3::get<confix_tag>(context))
if (iter == last || e != *iter++)
return false;
return true;
}
};
constexpr confix_value_matcher confix_value{};
}
#include <boost/fusion/include/adapt_struct.hpp>
namespace mytest
{
struct SimpleElement
{
std::string tag;
std::string content;
};
} // namespace bbspirit
BOOST_FUSION_ADAPT_STRUCT
(
mytest::SimpleElement, tag, content
)
#include <iostream>
int main()
{
namespace x3 = boost::spirit::x3;
for (auto text : { "[test]Hello World![/test]",
"[test]Hello [/World[/test]" }) {
std::cout << "text : " << text << '\n';
auto start = text, stop = text + std::strlen(text);
mytest::SimpleElement element;
auto const simpleTag
= x3e::confix(x3::lexeme['[' >> +~x3::char_(']') >> ']'],
x3::lexeme["[/" >> x3e::confix_value >> ']'])
[x3::lexeme[*x3::char_]];
bool result =
phrase_parse(start, stop, simpleTag, x3::ascii::space, element);
if (!result) {
std::cout << "failed to parse!\n";
}
else {
std::cout << "tag : " << element.tag << '\n';
std::cout << "content: " << element.content << '\n';
}
std::cout << '\n';
}
}输出:
text : [test]Hello World![/test]
tag : test
content: Hello World!
text : [test]Hello [/World[/test]
tag : test
content: Hello [/Worldhttps://stackoverflow.com/questions/62143841
复制相似问题