boost::spirit qi 解析运行时错误
boost::spirit qi parsing runtime error
为什么在使用该语法解析字符串时出现 运行时间错误?
template <typename Iterator, typename Skipper>
struct grammar : qi::grammar<Iterator, QVariant(), Skipper>
{
grammar() : grammar::base_type(object)
{
identifier = qi::raw[qi::lexeme[qi::alpha >> *(qi::alnum | '_' | ('-' >> qi::alnum))]];
self = (qi::raw[qi::lexeme["self"]]);
object = (self >> '.' >> identifier)
|(object >> '.' >> identifier); // there is no runtime error without that line
}
}
任何其他语法 运行 很好,但我想解析这样的东西:
self.foo.bar2.baz
处抛出运行时错误
qi::phrase_parse(it, str.end(), g, ascii::space, v) && it == str.end())
呼唤。
在我看来,作为起点的 object
规则必须声明为
qi::rule<It, QVariant(), Skipper> object;
虽然我不知道 QVariant 是什么,但我知道这个:
要使属性传播起作用,您需要使用内置的 Qi 转换试探法来实现属性类型兼容性。
对于第一个分支 (self>>'.'>>identifier
) 这/可能/足够简单了。假设 identifier
合成了一个字符串兼容的属性(例如 std::string
或 std::vector<char>
),那么生成的属性 可以 合法地分配为字符串。
样本
作为一个简单的例子,看看这个(我 "emulate" 类似于 QVariant
的东西):
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
using QVariant = boost::variant<std::string, int>;
template <typename Iterator, typename Skipper>
struct grammar : qi::grammar<Iterator, QVariant(), Skipper>
{
grammar() : grammar::base_type(object)
{
identifier = qi::raw[qi::lexeme[qi::alpha >> *(qi::alnum | '_' | ('-' >> qi::alnum))]];
self = (qi::raw[qi::lexeme["self"]]);
object =
qi::as_string [self >> '.' >> identifier]
//|qi::as_string [object >> '.' >> identifier] // there is no runtime error without that line
;
}
private:
qi::rule<Iterator, QVariant(), Skipper> object;
qi::rule<Iterator, std::string(), Skipper> identifier;
qi::rule<Iterator, std::string(), Skipper> self;
};
int main() {
using It = std::string::const_iterator;
std::string input = "self.foo.bar2.baz";
It f = input.begin(), l = input.end();
QVariant parsed;
bool ok = qi::phrase_parse(f, l, grammar<It, qi::space_type>{}, qi::space, parsed);
if (ok)
std::cout << "Parsed: " << parsed << "\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
正在打印:
Parsed: selffoo
Remaining unparsed: '.bar2.baz'
问题
第二个分支
qi::as_string [object >> '.' >> identifier]
必须合成为 tuple<QVariant, std::string>
才能与声明的其余部分保持一致。 Spirit 没有办法自动改变它。启发式系统可能开始抓住救命稻草,并尝试将绑定属性(记住,这是神秘的 QVariant)视为容器。如果它在这方面成功¹,事情就会编译。显然,在运行时,事情会崩溃,因为为 QVariant 的实际 - 运行时 - 值调用了不正确的接口。
这是理论。
解决方案?
查看工作演示,请注意排除了 '.'
。这使我怀疑您实际上不想要任何复杂的链式 "list" 对象取消引用,而是可能只想将整个匹配的输入 视为原始字符串 ?在这种情况下,最简单的解决方案是将 raw[]
提升一个级别,并可能使用字符串而不是 QVariant
.
¹ 例如因为 QVariant 接口有点 sloppy/unsafe 并直接在 variant 接口上公开 .begin/.end/value_type/insert 成员?
像"A = (A >> a ) | b" 这样的左递归在像boost::spirit 这样的LL 解析器中是不可用的。
它们应该被转换为 LL 友好的形式:
A = BR
R = aR |电子
其中 R - 新的非终端和 e - epsilon(空终端)。
为什么在使用该语法解析字符串时出现 运行时间错误?
template <typename Iterator, typename Skipper>
struct grammar : qi::grammar<Iterator, QVariant(), Skipper>
{
grammar() : grammar::base_type(object)
{
identifier = qi::raw[qi::lexeme[qi::alpha >> *(qi::alnum | '_' | ('-' >> qi::alnum))]];
self = (qi::raw[qi::lexeme["self"]]);
object = (self >> '.' >> identifier)
|(object >> '.' >> identifier); // there is no runtime error without that line
}
}
任何其他语法 运行 很好,但我想解析这样的东西:
self.foo.bar2.baz
处抛出运行时错误
qi::phrase_parse(it, str.end(), g, ascii::space, v) && it == str.end())
呼唤。
在我看来,作为起点的 object
规则必须声明为
qi::rule<It, QVariant(), Skipper> object;
虽然我不知道 QVariant 是什么,但我知道这个:
要使属性传播起作用,您需要使用内置的 Qi 转换试探法来实现属性类型兼容性。
对于第一个分支 (self>>'.'>>identifier
) 这/可能/足够简单了。假设 identifier
合成了一个字符串兼容的属性(例如 std::string
或 std::vector<char>
),那么生成的属性 可以 合法地分配为字符串。
样本
作为一个简单的例子,看看这个(我 "emulate" 类似于 QVariant
的东西):
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
using QVariant = boost::variant<std::string, int>;
template <typename Iterator, typename Skipper>
struct grammar : qi::grammar<Iterator, QVariant(), Skipper>
{
grammar() : grammar::base_type(object)
{
identifier = qi::raw[qi::lexeme[qi::alpha >> *(qi::alnum | '_' | ('-' >> qi::alnum))]];
self = (qi::raw[qi::lexeme["self"]]);
object =
qi::as_string [self >> '.' >> identifier]
//|qi::as_string [object >> '.' >> identifier] // there is no runtime error without that line
;
}
private:
qi::rule<Iterator, QVariant(), Skipper> object;
qi::rule<Iterator, std::string(), Skipper> identifier;
qi::rule<Iterator, std::string(), Skipper> self;
};
int main() {
using It = std::string::const_iterator;
std::string input = "self.foo.bar2.baz";
It f = input.begin(), l = input.end();
QVariant parsed;
bool ok = qi::phrase_parse(f, l, grammar<It, qi::space_type>{}, qi::space, parsed);
if (ok)
std::cout << "Parsed: " << parsed << "\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
正在打印:
Parsed: selffoo
Remaining unparsed: '.bar2.baz'
问题
第二个分支
qi::as_string [object >> '.' >> identifier]
必须合成为 tuple<QVariant, std::string>
才能与声明的其余部分保持一致。 Spirit 没有办法自动改变它。启发式系统可能开始抓住救命稻草,并尝试将绑定属性(记住,这是神秘的 QVariant)视为容器。如果它在这方面成功¹,事情就会编译。显然,在运行时,事情会崩溃,因为为 QVariant 的实际 - 运行时 - 值调用了不正确的接口。
这是理论。
解决方案?
查看工作演示,请注意排除了 '.'
。这使我怀疑您实际上不想要任何复杂的链式 "list" 对象取消引用,而是可能只想将整个匹配的输入 视为原始字符串 ?在这种情况下,最简单的解决方案是将 raw[]
提升一个级别,并可能使用字符串而不是 QVariant
.
¹ 例如因为 QVariant 接口有点 sloppy/unsafe 并直接在 variant 接口上公开 .begin/.end/value_type/insert 成员?
像"A = (A >> a ) | b" 这样的左递归在像boost::spirit 这样的LL 解析器中是不可用的。 它们应该被转换为 LL 友好的形式: A = BR R = aR |电子 其中 R - 新的非终端和 e - epsilon(空终端)。