标签:c boost boost-spirit
我正在尝试为参数列表编写一个解析器,这将允许以下内容:
myFunc( arg0, arg1, namedArg0 = valueA, namedArg1 = valueB )
在上面的例子中,我希望前两个参数解析为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" );
}
};
解决方法:
两个问题.首先,您要确保不匹配命名参数可以匹配的位置参数¹.
第二:你想要他们在不同的集合中.
In the above example, I would like the first two arguments to resolve to entities of
TypeA
, which would then be contained by astd::vector< TypeA >
. The second two arguments would resolve toTypeB
, which would be contained by astd::vector< TypeB >
. AllTypeA
arguments should come before allTypeB
arguments.
所以,你天真地写
argument_list = '(' >> -positional_args >> -named_args >> ')';
哪里
positional_args = expression % ',';
named_args = named_arg % ',';
named_arg = identifier >> '=' > expression)
当然你已经注意到这会因位置和命名args之间的可选插值而出错.但首先要做的事情.
让我们阻止位置匹配命名匹配的位置:
positional_args = (!named_arg >> expression) % ',';
这是相当生硬的.根据您精确的表达式/标识符产生,您可以使用更有效的区分器,但这是最简单的方法.
现在,为了继续本着相同的精神,关于位置/命名之间的’,’的最简单的事情是……只需检查如果有位置那么必须遵循或者),(其中然后应该被消费).很快:
argument_list = '(' >> positional_args >> -named_args >> ')';
positional_args = *(expression >> (&lit(')') | ','));
Note how
positional_args
now allows for empty match, so it’s no longer optional in theargument_list
rule
结果:
>你有你的语法
>它自然地解析为两个后续容器,如vector< TypeA>,vector< TypeB>
你还能想要什么?
异议,你的荣誉!
我几乎可以感觉到这种反应:我想要更优雅!
毕竟,现在,positional_args被“阻碍”了解有关命名args的知识以及期望参数列表的结束.
是.这是一个有效的问题.我同意更多涉及的语法,我更愿意写
argument_list = '(' >> -(argument % ',') >> ')';
这将自然地解析为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);
我认为这和上面一样笨拙,所以如果你能拥有你想要的自然载体,为什么会使AST复杂化?
吃你的蛋糕吧
是的,你可以将所有这些与大量的凤凰魔法相结合,或者使用自定义属性传播将参数类型排序到AST“桶”中. SO上有几个答案显示如何做那样的诡计.
> parsing into several vector members
但是,我认为对于提出的问题没有充分的理由来介绍其中任何一个.
¹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";
}
}
}
打印
======== () ========
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)'
标签:c,boost,boost-spirit 来源: https://codeday.me/bug/20190828/1754802.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。