(1) Say we want to parse a simple recursive block surrounded by {}
.
{
Some text.
{
{
Some more text.
}
Some Text again.
{}
}
}
This recursive parser is quite simple.
x3::rule<struct idBlock1> const ruleBlock1{"Block1"};
auto const ruleBlock1_def =
x3::lit('{') >>
*(
ruleBlock1 |
(x3::char_ - x3::lit('}'))
) >>
x3::lit('}');
BOOST_SPIRIT_DEFINE(ruleBlock1)
(2) Then the block becomes more complex. It could also be surrounded by []
.
{
Some text.
[
{
Some more text.
}
Some Text again.
[]
]
}
We need somewhere to store what kind of opening bracket that we have. Since x3 does not have locals, we may use attribute (x3::_val
) instead.
x3::rule<struct idBlock2, char> const ruleBlock2{"Block2"};
auto const ruleBlock2_def = x3::rule<struct _, char>{} =
(
x3::lit('{')[([](auto& ctx){x3::_val(ctx)='}';})] |
x3::lit('[')[([](auto& ctx){x3::_val(ctx)=']';})]
) >>
*(
ruleBlock2 |
(
x3::char_ -
(
x3::eps[([](auto& ctx){x3::_pass(ctx)='}'==x3::_val(ctx);})] >> x3::lit('}') |
x3::eps[([](auto& ctx){x3::_pass(ctx)=']'==x3::_val(ctx);})] >> x3::lit(']')
)
)
) >>
(
x3::eps[([](auto& ctx){x3::_pass(ctx)='}'==x3::_val(ctx);})] >> x3::lit('}') |
x3::eps[([](auto& ctx){x3::_pass(ctx)=']'==x3::_val(ctx);})] >> x3::lit(']')
);
BOOST_SPIRIT_DEFINE(ruleBlock2)
(3) The block content (surrounded part), we call it argument, may be much more complicated than this example. So we decide to create a rule for it. This attribute solution is not working in this case. Luckily we still have x3::with
directive. We can save the open bracket (or expecting close bracket) in a stack reference and pass it to the next level.
struct SBlockEndTag {};
x3::rule<struct idBlockEnd> const ruleBlockEnd{"BlockEnd"};
x3::rule<struct idArg> const ruleArg{"Arg"};
x3::rule<struct idBlock3> const ruleBlock3{"Block3"};
auto const ruleBlockEnd_def =
x3::eps[([](auto& ctx){
assert(!x3::get<SBlockEndTag>(ctx).get().empty());
x3::_pass(ctx)='}'==x3::get<SBlockEndTag>(ctx).get().top();
})] >>
x3::lit('}')
|
x3::eps[([](auto& ctx){
assert(!x3::get<SBlockEndTag>(ctx).get().empty());
x3::_pass(ctx)=']'==x3::get<SBlockEndTag>(ctx).get().top();
})] >>
x3::lit(']');
auto const ruleArg_def =
*(
ruleBlock3 |
(x3::char_ - ruleBlockEnd)
);
auto const ruleBlock3_def =
(
x3::lit('{')[([](auto& ctx){x3::get<SBlockEndTag>(ctx).get().push('}');})] |
x3::lit('[')[([](auto& ctx){x3::get<SBlockEndTag>(ctx).get().push(']');})]
) >>
ruleArg >>
ruleBlockEnd[([](auto& ctx){
assert(!x3::get<SBlockEndTag>(ctx).get().empty());
x3::get<SBlockEndTag>(ctx).get().pop();
})];
BOOST_SPIRIT_DEFINE(ruleBlockEnd, ruleArg, ruleBlock3)
The code is on Coliru.
Question: is this how we write recursive x3 parser for this kind of problem? With spirit Qi's locals and inherited attributes, the solution seems to be much simpler. Thanks.