pt3*_*Nyc 3 c++ boost boost-spirit
我正在尝试为参数列表编写一个解析器,这将允许以下内容:
myFunc( arg0, arg1, namedArg0 = valueA, namedArg1 = valueB )
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,我希望前两个参数解析为TypeA的实体,然后由std :: vector <TypeA>包含.后两个参数将解析为TypeB,它将由std :: vector <TypeB>包含.所有TypeA参数都应该在所有TypeB参数之前.但是我想从一个以逗号分隔的列表中解析所有内容.应该可以只有TypeA参数,只有TypeB参数或TypeA元素序列,后跟一系列TypeB元素.
我无法定义规则,以便将最终的TypeA参数与第一个TypeB参数分开的逗号不会被误认为是另一个TypeA参数的期望.
我目前的实施情况如下.任何人都可以就如何解决这个问题提出任何建议吗?
这里的关键区别是TypeA参数应该是单个符号,而TypeB参数应采用以下形式:symbol = symbol.
问题似乎与TypeA参数等同于TypeB参数的第一部分这一事实有关,因此使得TypeA序列的结尾不清楚?
谢谢!
struct Params
{
std::vector<TypeA> a_elements;
std::vector<TypeB> b_elements;
Params(const std::vector<TypeA>& a_vec, const std::vector<TypeB>& b_vec)
: a_elements( a_vec ), b_elements( b_vec ) {}
static Params create(const std::vector<TypeA>& a_vec, const std::vector<TypeB>& b_vec)
{
return Params( a_vec, b_vec );
}
};
struct ParamsParser : qi::grammar<Iterator, Params(), Skipper>
{
qi::rule<Iterator, Params(), Skipper> start_rule;
qi::rule<Iterator, std::vector<TypeA>(), Skipper> type_a_vec_opt_rule;
qi::rule<Iterator, std::vector<TypeB>(), Skipper> type_b_vec_opt_rule;
qi::rule<Iterator, std::vector<TypeA>(), Skipper> type_a_vec_rule;
qi::rule<Iterator, std::vector<TypeB>(), Skipper> type_b_vec_rule;
qi::rule<Iterator, TypeA(), Skipper> type_a_rule;
qi::rule<Iterator, TypeB(), Skipper> type_b_rule;
qi::rule<Iterator, std::string(), Skipper> symbol_rule;
ParamsParser() : ParamsParser::base_type( start_rule, "params_parser" )
{
start_rule =
// version 1:
( ( '(' >> type_a_vec_rule >> ',' >> type_b_vec_rule >> ')' )
[ qi::_val = boost::phoenix::bind( Params::create, qi::_1, qi::_2 ) ] )
// version 2:
| ( ( '(' >> type_a_vec_opt_rule >> ')' )
[ qi::_val = boost::phoenix::bind( Params::create, qi::_1, std::vector<TypeB>() ) ] )
// version 3:
| ( ( '(' >> type_b_vec_opt_rule >> ')' )
[ qi::_val = boost::phoenix::bind( Params::create, std::vector<TypeA>(), qi::_1 ) ] )
;
type_a_vec_opt_rule = -type_a_vec_rule;
type_b_vec_opt_rule = -type_b_vec_rule;
type_a_vec_rule = ( type_a_rule % ',' );
type_b_vec_rule = ( type_b_rule % ',' );
type_a_rule = ( symbol_rule );
type_b_rule = ( symbol_rule >> '=' >> symbol_rule );
symbol_rule = qi::char_( "a-zA-Z_" ) >> *qi::char_( "a-zA-Z_0-9" );
}
};
Run Code Online (Sandbox Code Playgroud)
两个问题.首先,您要确保不匹配命名参数可以匹配的位置参数¹.
第二:你想要他们在不同的集合中.
在上面的例子中,我希望前两个参数解析为实体
TypeA,然后由a包含std::vector< TypeA >.后两个论点将解决TypeB,其中包含一个std::vector< TypeB >.所有TypeA论据都应该在所有TypeB论点之前.
所以,你天真地写
argument_list = '(' >> -positional_args >> -named_args >> ')';
Run Code Online (Sandbox Code Playgroud)
哪里
positional_args = expression % ',';
named_args = named_arg % ',';
named_arg = identifier >> '=' > expression)
Run Code Online (Sandbox Code Playgroud)
当然你已经注意到这会因位置和命名args之间的可选插值而出错.但首先要做的事情.
让我们阻止位置匹配命名匹配的位置:
positional_args = (!named_arg >> expression) % ',';
Run Code Online (Sandbox Code Playgroud)
这是相当生硬的.根据您精确的表达式/标识符产生,您可以使用更有效的区分器,但这是最简单的方法.
现在,为了继续本着相同的精神,关于','位置之间/命名之间可以起作用的最简单的事情是......简单地检查如果存在位置那么必须遵循任何一个)或,(然后应该被消耗) .很快:
argument_list = '(' >> positional_args >> -named_args >> ')';
positional_args = *(expression >> (&lit(')') | ','));
Run Code Online (Sandbox Code Playgroud)
注意
positional_args现在如何允许空匹配,因此它在argument_list规则中不再是可选的
vector<TypeA>,vector<TypeB>你还能想要什么?
我几乎可以感觉到这种反应:我想要更优雅!毕竟,现在positional_args对于命名args的知识以及期望参数列表的结束而"受阻".
是.这是一个有效的问题.我同意更多涉及的语法,我更愿意写
argument_list = '(' >> -(argument % ',') >> ')';
Run Code Online (Sandbox Code Playgroud)
然后,这将自然地解析为TypeAOrB² 的容器,并且您将进行一些语义检查以确保位置参数之后没有位置参数:
arguments %=
eps [ found_named_arg = false ] // initialize the local (_a)
>> '(' >> -(argument(found_named_arg) % ',') >> ')';
argument %= (named_arg > eps [named_only=true])
| (eps(!named_only) >> positional);
Run Code Online (Sandbox Code Playgroud)
我认为这和上面一样笨拙,所以如果你能拥有你想要的自然载体,为什么会使AST复杂化?
是的,你可以将所有这些与大量的凤凰魔法相结合,或者使用自定义属性传播将参数类型排序到AST"桶"中.SO上有几个答案显示如何做那样的诡计.
但是,我认为对于提出的问题没有充分的理由来介绍其中任何一个.
¹OT rant:为什么不使用正确的术语而不是像"typeA"和"typeB"那样的烟雾和镜子.如果它真的是一个秘密,不要在SO上发布问题.如果不是,请不要隐藏上下文,因为99%的时间问题和解决方案都遵循上下文
² boost::variant<TypeA, TypeB>
³ Live On Coliru反比特用途的完整来源:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
struct Positional { };
struct Named { };
using Arg = boost::variant<Positional, Named>;
using Args = std::vector<Arg>;
template <typename Iterator>
struct ArgsParser : qi::grammar<Iterator, Args()>
{
ArgsParser() : ArgsParser::base_type( start_rule, "params_parser" )
{
using namespace qi;
start_rule = skip(space) [ arguments ];
arguments %=
eps [ found_named_arg = false ] // initialize the local (_a)
>> '(' >> -(argument(found_named_arg) % ',') >> ')';
argument %= (named_arg > eps [named_only=true])
| (eps(!named_only) >> positional);
named_arg = "n" >> attr(Named{});
positional = "p" >> attr(Positional{});
}
private:
using Skipper = qi::space_type;
qi::rule<Iterator, Args()> start_rule;
qi::rule<Iterator, Args(), Skipper, qi::locals<bool> > arguments;
qi::rule<Iterator, Arg(bool&), Skipper> argument;
qi::rule<Iterator, Named(), Skipper> named_arg;
qi::rule<Iterator, Positional(), Skipper> positional;
qi::_a_type found_named_arg;
qi::_r1_type named_only;
};
// for debug output
static inline std::ostream& operator<<(std::ostream& os, Named) { return os << "named"; }
static inline std::ostream& operator<<(std::ostream& os, Positional) { return os << "positional"; }
int main() {
using It = std::string::const_iterator;
ArgsParser<It> const p;
for (std::string const input : {
"()",
"(p)",
"(p,p)",
"(p,n)",
"(n,n)",
"(n)",
// start the failing
"(n,p)",
"(p,p,n,p,n)",
"(p,p,n,p)",
})
{
std::cout << " ======== " << input << " ========\n";
It f(input.begin()), l(input.end());
Args parsed;
if (parse(f,l,p,parsed)) {
std::cout << "Parsed " << parsed.size() << " arguments in list: ";
std::copy(parsed.begin(), parsed.end(), std::ostream_iterator<Arg>(std::cout, " "));
std::cout << "\n";
} else {
std::cout << "Parse failed\n";
}
if (f!=l) {
std::cout << "Remaining input unparsed: '" << std::string(f,l) << "'\n";
}
}
}
Run Code Online (Sandbox Code Playgroud)
打印
======== () ========
Parsed 0 arguments in list:
======== (p) ========
Parsed 1 arguments in list: positional
======== (p,p) ========
Parsed 2 arguments in list: positional positional
======== (p,n) ========
Parsed 2 arguments in list: positional named
======== (n,n) ========
Parsed 2 arguments in list: named named
======== (n) ========
Parsed 1 arguments in list: named
======== (n,p) ========
Parse failed
Remaining input unparsed: '(n,p)'
======== (p,p,n,p,n) ========
Parse failed
Remaining input unparsed: '(p,p,n,p,n)'
======== (p,p,n,p) ========
Parse failed
Remaining input unparsed: '(p,p,n,p)'
Run Code Online (Sandbox Code Playgroud)