Boost Spirit x3:解析为结构体
来自 Boost Spirit X3 教程:
From the Boost Spirit X3 tutorial:
首先,让我们创建一个代表员工的结构体:
First, let's create a struct representing an employee:
namespace client { namespace ast
{
struct employee
{
int age;
std::string surname;
std::string forename;
double salary;
};
}}
然后,我们需要将我们的员工结构告诉 Boost.Fusion 以使其成为语法可以利用的一流融合公民.
Then, we need to tell Boost.Fusion about our employee struct to make it a first-class fusion citizen that the grammar can utilize.
BOOST_FUSION_ADAPT_STRUCT(
client::ast::employee,
(int, age)
(std::string, surname)
(std::string, forename)
(double, salary)
)`
[...]在fusion 看来,结构体只是元组的一种形式.你可以适应任何struct 是一个完全符合的融合元组.[...]应用我们上面的折叠规则,RHS 具有以下属性:fusion::vector
结构员工与融合::矢量.所以,start 的 RHS 就地使用了 start 的属性(一个结构体员工)当它开始工作时.
[...]
In fusion's view, a struct is just a form of a tuple. You can adapt any
struct to be a fully conforming fusion tuple.
[...]
Applying our collapsing rules above, the RHS has an attribute of:
fusion::vector<int, std::string, std::string, double>
The struct employee IS compatible with
fusion::vector.
So, the RHS of start uses start's attribute (a struct employee) in-situ
when it does its work.
如果我很好理解,这个逻辑在很大程度上依赖于属性的顺序.
If I well understood, this logic heavily relies on the order of the attributes.
现在,我需要解析类似的东西
Now, I am in a situation where I need to parse something like
Layer "L1" {
number = 23
color = green
visible = true
}
结构化
struct LayerInfo
{
std::string layerName;
int layerNumber;
std::string color;
bool visible;
}
问题是,图层属性的顺序可以改变,这与上面看到的逻辑相反.
The problem is, the order of the layer properties can change, which is in opposition with the logic seen above.
解析这样的结构的正确方法是什么?我是否一定需要使用语义动作?
Which is the correct way to parse into a struct like this? Do I need necessarily need to use semantic actions?
推荐答案
我喜欢@llonesmiz 在评论中的方法.
I love @llonesmiz's approach in the comment.
不过,我不得不"尝试使用 X3 的我最喜欢的方法,也使用函数组合.这是解析和传播值的方法的草图.
I "had to" try my favorite approach with X3 using functional composition too, though. Here's a sketch of the approach which does parse and propagate the values.
缺少对属性存在/唯一性的检查.(我认为这样的事情可以使用 x3::with<>
上下文添加来实现,它基本上包含一个 std::set<VT::*>
.当然,这样的事情需要(依赖于实现?)强制转换或擦除包装器).
Missing are checks on property presence/uniqueness. (I think such a thing is doable using a
x3::with<>
context addition that basically contains astd::set<V T::*>
. Of course such a thing needs (implementation dependent?) casts or an erasure wrapper).
暂时不加评论:
生活在 Coliru强>
#include <iostream>
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
struct LayerInfo
{
std::string layerName;
int layerNumber = 0;
std::string color;
bool visible = false;
};
namespace Parser {
namespace x3 = boost::spirit::x3;
// custom type parsers
auto quoted = rule<std::string>("quoted", x3::lexeme [ '"' >> *('\' >> x3::char_ | ~x3::char_('"')) >> '"' ]);
struct colors_type : x3::symbols<char> {
colors_type() {
this->add("red")("blue")("green")("black");
}
} static const colors;
namespace detail {
template <typename T> auto propagate(T member) {
return [=](auto& ctx){ x3::traits::move_to(x3::_attr(ctx), x3::_val(ctx).*member); };
}
template <typename T> auto make_member_parser(int T::* const member) { return x3::int_ [propagate(member)]; }
template <typename T> auto make_member_parser(bool T::* const member) { return x3::bool_ [propagate(member)]; }
template <typename T> auto make_member_parser(std::string T::* const member) { return x3::raw[colors] [propagate(member)]; }
template <typename T = LayerInfo, typename P>
auto rule(const char* debug, P p) { return x3::rule<struct _, T> {debug} = x3::skip(x3::space)[p]; };
auto property = [](auto label, auto member) {
return rule(label, x3::as_parser(label) >> '=' >> make_member_parser(member));
};
}
using detail::rule;
using detail::propagate;
using detail::property;
auto name = rule("name", "Layer" >> quoted [propagate(&LayerInfo::layerName)]);
auto number = property("number", &LayerInfo::layerNumber);
auto color = property("color", &LayerInfo::color);
auto visible = property("visible", &LayerInfo::visible);
auto layer_info = name >> '{' >> +(number | color | visible) >> '}';
auto grammar = rule("layer_info", layer_info);
}
std::ostream& operator<<(std::ostream& os, LayerInfo const& li) {
return os << "LayerInfo "" << li.layerName << ""{"
<< "number=" << li.layerNumber << " "
<< "color=" << li.color << " "
<< "visible=" << std::boolalpha << li.visible
<< "}
";
}
int main() {
std::string const sample = R"(Layer "L1" {
number = 23
color = green
visible = true
})";
LayerInfo v;
auto f = sample.begin(), l = sample.end();
bool ok = parse(f, l, Parser::grammar, v);
if (ok)
std::cout << "Parsed: " << v << "
";
else
std::cout << "Parse failed
";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'
";
}
印刷品
Parsed: LayerInfo "L1"{number=23 color=green visible=true}
机智调试信息:生活在 Coliru
<layer_info>
<try>Layer "L1" {
num</try>
<name>
<try>Layer "L1" {
num</try>
<quoted>
<try> "L1" {
number =</try>
<success> {
number = 23
</success>
<attributes>[L, 1]</attributes>
</quoted>
<success> {
number = 23
</success>
<attributes>LayerInfo "L1"{number=0 color= visible=false}
</attributes>
</name>
<number>
<try>
number = 23
</try>
<success>
color = green
</success>
<attributes>LayerInfo "L1"{number=23 color= visible=false}
</attributes>
</number>
<number>
<try>
color = green
</try>
<fail/>
</number>
<color>
<try>
color = green
</try>
<success>
visible = true
</success>
<attributes>LayerInfo "L1"{number=23 color=green visible=false}
</attributes>
</color>
<number>
<try>
visible = true
</try>
<fail/>
</number>
<color>
<try>
visible = true
</try>
<fail/>
</color>
<visible>
<try>
visible = true
</try>
<success>
}</success>
<attributes>LayerInfo "L1"{number=23 color=green visible=true}
</attributes>
</visible>
<number>
<try>
}</try>
<fail/>
</number>
<color>
<try>
}</try>
<fail/>
</color>
<visible>
<try>
}</try>
<fail/>
</visible>
<success></success>
<attributes>LayerInfo "L1"{number=23 color=green visible=true}
</attributes>
</layer_info>
相关文章