我正在尝试我的第一个精神解析器,似乎我遇到了属性传播问题。尝试 plantuml 语法。如果我破解它就该死,如果我不破解就该死......
完整要点在这里:https://coliru.stacked-crooked.com/a/023ec430b509e1be
它的本质,我认为是这样的:
using ast_node = boost::variant<
ast_null,
ast_transition,
boost::recursive_wrapper<ast_state>,
boost::recursive_wrapper<ast_region>,
ast_machine
>;
using ast_nodes_t = std::vector<ast_node>;
struct ast_transition
{
//std::string _id;
std::string _fromState;
std::string _toState;
std::string _event;
std::string _guard;
std::string _effect;
};
BOOST_FUSION_ADAPT_STRUCT(
ast_transition,
//(std::string, _id)
(std::string, _fromState)
(std::string, _toState)
(std::string, _event)
(std::string, _guard)
(std::string, _effect)
)
struct ast_region
{
ast_nodes_t _subtree;
};
BOOST_FUSION_ADAPT_STRUCT(
ast_region,
(ast_nodes_t, _subtree)
)
// possible "fix" for "error: no type named 'value_type' in 'struct ast_region'" but causes infinite matching loop on region(s)
//region = eps >> *transition
region = *transition
;
bs::qi::rule<ITER, ast_transition(), SKIPPER> transition;
bs::qi::rule<ITER, ast_region(), SKIPPER> region;
bs::qi::rule<ITER, ast_nodes_t(), SKIPPER> regions;
这给出了:
/usr/local/include/boost/spirit/home/support/container.hpp:130:12: error: no type named 'value_type' in 'struct ast_region'
任何如何调整容器属性的想法将不胜感激,谢谢!
我试图根据 plantuml 状态机描述进行建模:
我只能假设您遗漏了代码来解释位置跟踪基础和迭代器传递(我希望您使用
on_sucess
,而不仅仅是on_error
)。
我简化/重塑了事物以便进行审查。最后,我认为唯一真正需要的改变似乎是您已经评论过的:
regions = *region;
原理请参见
现在无限循环表明
region
与空输入匹配。由于过渡至少需要 rstring
,而这至少需要一个字符,因此您应该能够通过需要至少一个过渡来解决此问题:
region = qi::eps >> +transition;
我最终得到了这个示例,它还以我知道的最节能的方式修复了调试输出:
#define BOOST_SPIRIT_DEBUG 1
#include <boost/fusion/include/adapted.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_line_pos_iterator.hpp>
#include <fstream>
#include <iomanip>
namespace bs = boost::spirit;
namespace bp = boost::phoenix;
namespace Ast {
/*
- a state machine is a collection of regions
- a region is a collection of states & transitions
- a state is a collection of regions & transitions
*/
struct state;
struct region;
struct machine;
struct location {
size_t _line{0};
size_t _col{0};
std::string _file;
};
using node_base = location;
// struct node_base : location {};
struct null : location {};
struct transition : location {
std::string /*_id,*/ _fromState, _toState, _event, _guard, _effect;
};
using node = boost::variant< //
null, transition, //
boost::recursive_wrapper<state>, //
boost::recursive_wrapper<region>, //
boost::recursive_wrapper<machine>>;
using nodes_t = std::vector<node>;
struct state : location {
nodes_t _subtree;
};
struct region : location {
nodes_t _subtree;
};
struct machine : location {
nodes_t _subtree;
};
using boost::fusion::operator<<;
} // namespace Ast
BOOST_FUSION_ADAPT_STRUCT(Ast::null) // or provide operator<<...
BOOST_FUSION_ADAPT_STRUCT(Ast::machine, _subtree)
BOOST_FUSION_ADAPT_STRUCT(Ast::region, _subtree)
BOOST_FUSION_ADAPT_STRUCT(Ast::state, _subtree)
BOOST_FUSION_ADAPT_STRUCT(Ast::transition, /*_id,*/ _fromState, _toState, _event, _guard, _effect)
BOOST_FUSION_ADAPT_STRUCT(Ast::node_base, _line, _col, _file)
namespace plantuml {
namespace qi = bs::qi;
namespace encoding = qi::ascii;
template <typename It> struct skipper final : qi::grammar<It> {
skipper() : skipper::base_type(rule) {}
qi::rule<It> const rule = encoding::space | ("#" >> *~encoding::char_("\n") >> -qi::eol) |
("//" >> *~encoding::char_("\n") >> -qi::eol) | ("/*" >> *(encoding::char_ - "*/") >> "*/");
};
template <typename ITER, typename SKIPPER>
struct plantuml_grammar final
: qi::grammar<ITER, Ast::machine(), qi::locals<std::string>, SKIPPER /*bs::ascii::space_type*/> {
plantuml_grammar(ITER first) : plantuml_grammar::base_type(start) {
using qi::fail;
using qi::on_error;
using namespace qi::labels; // _a, _1, _val, _pass ...
qstring %= qi::lexeme['"' >> +(qi::char_ - '"') >> '"'];
rstring %= qi::raw[qi::lexeme[+qi::char_("a-zA-Z0-9_")]];
transition %= rstring >> qi::lit("-->") >> rstring;
region = qi::eps >> +transition;
regions = *region;
start = qi::lit("@startuml") //[ bp::push_back(bp::ref(_val._subtree), Ast::region()) ]
>> regions >> qi::lit("@enduml");
BOOST_SPIRIT_DEBUG_NODES((start)(regions)(region)(transition))
}
qi::rule<ITER, std::string()> qstring;
qi::rule<ITER, std::string()> rstring;
qi::rule<ITER, Ast::transition(), SKIPPER> transition;
qi::rule<ITER, Ast::region(), SKIPPER> region;
qi::rule<ITER, Ast::nodes_t(), SKIPPER> regions;
qi::rule<ITER, Ast::machine(), qi::locals<std::string>, SKIPPER> start;
}; // plantuml_grammar
bool plantuml_parser(std::istream& in) {
using base_iter_t = bs::istream_iterator;
using lp_iter_t = bs::line_pos_iterator<base_iter_t>;
using in_iter_t = lp_iter_t; // base_iter_t; //
using skipper_t = skipper<in_iter_t>; //
using plantuml_grammar_t = plantuml_grammar<in_iter_t, skipper_t>;
in_iter_t crtIt(base_iter_t(in >> std::noskipws));
in_iter_t firstIt(crtIt);
in_iter_t endIt;
plantuml_grammar_t grammar(firstIt);
Ast::node ast;
skipper_t skip = {};
bool match = qi::phrase_parse(crtIt, endIt, grammar,
skip, // bs::ascii::space,
ast);
return match;
}
} // namespace plantuml
int main() {
std::string const test = R"--(
@startuml
#xDeploy -down-> xOperation
Deploy --> Operation
/*
state Operation {
[*] -down-> Operation_Launch
Operation_Launch -down-> Operation_Auto_Monitor
--
[*] -down-> Operation_BizData_Collection
Operation_BizData_Collection -down-> Operation_BizData_Anylasis
--
[*] -down-> Operation_Next_Preparation
}
Operation -down-> [*]
*/
@enduml
)--";
// bool ret = plantuml::plantuml_parser(test);
std::istringstream in(test);
bool ret = plantuml::plantuml_parser(in);
}
印刷
<start>
<try>\n @startuml\n </try>
<regions>
<try>\n \n #x</try>
<region>
<try>\n \n #x</try>
<transition>
<try>Deploy --> Operation</try>
<success>\n \n /*</success>
<attributes>[[[D, e, p, l, o, y], [O, p, e, r, a, t, i, o, n], [], [], []]]</attributes>
</transition>
<transition>
<try>\n \n /*</try>
<fail/>
</transition>
<success>\n \n /*</success>
<attributes>[[[[[D, e, p, l, o, y], [O, p, e, r, a, t, i, o, n], [], [], []]]]]</attributes>
</region>
<region>
<try>\n \n /*</try>
<transition>
<try>@enduml\n </try>
<fail/>
</transition>
<fail/>
</region>
<success>\n \n /*</success>
<attributes>[[[[[[D, e, p, l, o, y], [O, p, e, r, a, t, i, o, n], [], [], []]]]]]</attributes>
</regions>
<success>\n </success>
<attributes>[[[[[[[D, e, p, l, o, y], [O, p, e, r, a, t, i, o, n], [], [], []]]]]]]</attributes><locals>()</locals>
</start>