x3语法中无用的编译器错误 [英] Unhelpful compiler errors in x3 grammar
问题描述
以下用于简单机器人命令语言的Spirit x3语法在Windows Visual Studio 17中生成编译器错误.对于此项目,我需要将警告级别编译为4(/W4),并将警告视为错误(/WX). ).
The following Spirit x3 grammar for a simple robot command language generates compiler errors in Windows Visual Studio 17. For this project, I am required to compile with the warning level to 4 (/W4) and treat warnings as errors (/WX).
警告C4127条件表达式为 常量SpiritTest e:\ data \ boost \ boost_1_65_1 \ boost \ spirit \ home \ x3 \ char \ detail \ cast_char.hpp 29
错误C2039插入":不是以下成员 'boost :: spirit :: x3 :: unused_type'SpiritTest e:\ data \ boost \ boost_1_65_1 \ boost \ spirit \ home \ x3 \ core \ detail \ parse_into_container.hpp 259错误C2039'end':不是成员 'boost :: spirit :: x3 :: unused_type'SpiritTest e:\ data \ boost \ boost_1_65_1 \ boost \ spirit \ home \ x3 \ core \ detail \ parse_into_container.hpp 259错误C2039'empty':不是成员 'boost :: spirit :: x3 :: unused_type'SpiritTest e:\ data \ boost \ boost_1_65_1 \ boost \ spirit \ home \ x3 \ core \ detail \ parse_into_container.hpp 254错误C2039'begin':不属于 'boost :: spirit :: x3 :: unused_type'SpiritTest e:\ data \ boost \ boost_1_65_1 \ boost \ spirit \ home \ x3 \ core \ detail \ parse_into_container.hpp 259
Warning C4127 conditional expression is constant SpiritTest e:\data\boost\boost_1_65_1\boost\spirit\home\x3\char\detail\cast_char.hpp 29
Error C2039 'insert': is not a member of 'boost::spirit::x3::unused_type' SpiritTest e:\data\boost\boost_1_65_1\boost\spirit\home\x3\core\detail\parse_into_container.hpp 259 Error C2039 'end': is not a member of 'boost::spirit::x3::unused_type' SpiritTest e:\data\boost\boost_1_65_1\boost\spirit\home\x3\core\detail\parse_into_container.hpp 259 Error C2039 'empty': is not a member of 'boost::spirit::x3::unused_type' SpiritTest e:\data\boost\boost_1_65_1\boost\spirit\home\x3\core\detail\parse_into_container.hpp 254 Error C2039 'begin': is not a member of 'boost::spirit::x3::unused_type' SpiritTest e:\data\boost\boost_1_65_1\boost\spirit\home\x3\core\detail\parse_into_container.hpp 259
很明显,我的语法有问题,但是错误消息完全没有帮助.我发现,如果我删除语法的最后一行中的Kleene星号(*参数为正参数),错误消失了,但是我得到了很多这样的警告:
Clearly, something is wrong with my grammar, but the error messages are completely unhelpful. I have found that if I remove the Kleene star in the last line of the grammar (*parameter to just parameter) the errors disappear, but then I get lots of warnings like this:
警告C4459声明数字"隐藏全局 声明SpiritTest e:\ data \ boost \ boost_1_65_1 \ boost \ spirit \ home \ x3 \ support \ numeric_utils \ detail \ extract_int.hpp 174 警告C4127条件表达式为常量SpiritTest e:\ data \ boost \ boost_1_65_1 \ boost \ spirit \ home \ x3 \ char \ detail \ cast_char.hpp 29
Warning C4459 declaration of 'digit' hides global declaration SpiritTest e:\data\boost\boost_1_65_1\boost\spirit\home\x3\support\numeric_utils\detail\extract_int.hpp 174 Warning C4127 conditional expression is constant SpiritTest e:\data\boost\boost_1_65_1\boost\spirit\home\x3\char\detail\cast_char.hpp 29
#include <string>
#include <iostream>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;
//
// Grammar for simple command language
//
namespace scl
{
using boost::spirit::x3::char_;
using boost::spirit::x3::double_;
using boost::spirit::x3::int_;
using boost::spirit::x3::lexeme;
using boost::spirit::x3::lit;
using boost::spirit::x3::no_case;
auto valid_identifier_chars = char_ ("a-zA-Z_");
auto quoted_string = '"' >> *(lexeme [~char_ ('"')]) >> '"';
auto keyword_value_chars = char_ ("a-zA-Z0-9$_.");
auto qual = lexeme [!(no_case [lit ("no")]) >> +valid_identifier_chars] >> -('=' >> (quoted_string | int_ | double_ | +keyword_value_chars));
auto neg_qual = lexeme [no_case [lit ("no")] >> +valid_identifier_chars];
auto qualifier = lexeme ['/' >> (qual | neg_qual)];
auto verb = +valid_identifier_chars >> *qualifier;
auto parameter = +keyword_value_chars >> *qualifier;
auto command = verb >> *parameter;
}; // End namespace scl
using namespace std; // Must be after Boost stuff!
int
main ()
{
vector <string> input =
{
"show/out=\"somefile.txt\" motors/all cameras/full",
"start/speed=5 motors arm1 arm2/speed=2.5/track arm3",
"rotate camera1/notrack/axis=y/angle=45"
};
//
// Parse each of the strings in the input vector
//
for (string str : input)
{
auto b = str.begin ();
auto e = str.end ();
cout << "Parsing: " << str << endl;
x3::phrase_parse (b, e, scl::command, x3::space);
if (b != e)
{
cout << "Error, only parsed to position: " << b - str.begin () << endl;
}
} // End for
return 0;
} // End main
推荐答案
自Boost 1.65起存在回归,导致某些规则出现问题,这些规则可能传播到容器类型属性中.
There is a regression since Boost 1.65 that causes problems with some rules that potentially propagate into container type attributes.
在没有实际绑定属性的情况下实例化时,它们将分派到错误的重载.发生这种情况时,将有一个名为unused_type
的模拟"属性类型.您看到的错误表明unused_type
被当作一种具体的属性类型来对待,并且显然不会生效.
They dispatch to the wrong overload when instantiated without an actual bound attribute. When this happens there is a "mock" attribute type called unused_type
. The errors you are seeing indicate that unused_type
is being treated as if it were a concrete attribute type, and clearly that won't fly.
回归已在 https://github.com/boostorg/spirit/commit/中修复. ee4943d5891bdae0706fb616b908e3bf528e0dfa
通过Boost 1.64编译可以看到它是回归:
You can see that it's a regression by compiling with Boost 1.64:
-
Boost 1.64将其编译为 fine GCC 和Clang
Boost 1.64 compiles it fine GCC and Clang
提升1.65会破坏它 GCC 和
Boost 1.65 breaks it GCC and Clang again
现在,最新的开发应该可以修复它,但是您可以简单地复制补丁文件,甚至只是7行补丁.
Now, latest develop is supposed to fix it, but you can simply copy the patched file, even just the 7-line patch.
All of the above was already available when I linked the duplicate question How to make a recursive rule in boost spirit x3 in VS2017, which highlights the same regression
评论
-
using namespace std; // Must be after Boost stuff!
实际上,除非非常局限范围,否则它可能应该无处不在,您可以在其中看到任何潜在的名称病原菌的影响.
Actually, it probably needs to be nowhere unless very locally scoped, where you can see the impact of any potential name colisions.
考虑将船长封装起来,因为从逻辑上讲,它可能是语法规范的一部分,而不是调用者可以忽略的东西.
Consider encapsulating the skipper, since it's likely logically part of your grammar spec, not something to be overridden by the caller.
这是一个错误:
auto quoted_string = '"' >> *(lexeme[~char_('"')]) >> '"';
您可能想断言整个文字是lexeme,而不是单个字符(这很重要,因为空格永远不会因为解析器而击中解析器).
You probably meant to assert the whole literal is lexeme, not individual characters (that's... moot because whitespace would never hit the parser anyways, because of the skipper).
auto quoted_string = lexeme['"' >> *~char_('"') >> '"'];
-
同样,您可能希望
+keyword_value_chars
成为词素,因为现在one=two three four
会解析限定词"one
且其关键字值"为,而不是 one three four
¹ Likewise, you might have intended
+keyword_value_chars
to be lexeme, because right nowone=two three four
would parse the "qualifier"one
with a "keyword value" ofonethreefour
, notone three four
¹x3::space
跳过嵌入的换行符,如果不是这样,请使用x3::blank
x3::space
skips embedded newlines, if that's not the intent, usex3::blank
由于PEG语法是从左至右解析贪婪的,因此您可以订购
qualifier
生成词,而无需执行!(no_case["no"])
前瞻性断言.这不仅消除了重复,而且使语法更简单,更有效:Since PEG grammars are parsed left-to-right greedy, you can order the
qualifier
production and do without the!(no_case["no"])
lookahead assertion. That not only removes duplication but also makes the grammar simpler and more efficient:auto qual = lexeme[+valid_identifier_chars] >> -('=' >> (quoted_string | int_ | double_ | +keyword_value_chars)); // TODO lexeme auto neg_qual = lexeme[no_case["no"] >> +valid_identifier_chars]; auto qualifier = lexeme['/' >> (neg_qual | qual)];
¹注意(后圣经),因为我们注意到
qualifier
本身已经是一个词素,因此无需lexeme[]
里面的东西(当然,除非它们被重用)在使用船长的情况下.)¹ Note (Post-Scriptum) now that we notice
qualifier
is, itself, already a lexeme, there's no need tolexeme[]
things inside (unless, of course they're reused in contexts with skippers).但是,这也引起了一个问题,即是否应该接受
=
运算符周围的空格(当前不是),或者是否可以使用空格分隔限定符(例如id /a /b
;目前可以).However, this also gives rise to the question whether whitespace around the
=
operator should be accepted (currently, it is not), or whether qualifiers can be separated with whitespace (likeid /a /b
; currently they can).-
也许
verb
也需要一些lexemes[]
(除非您真的 did 想要将"one two three"
解析为动词) Perhaps
verb
needed somelexemes[]
as well (unless you really did want to parse"one two three"
as a verb)如果
no
前缀为否定限定词,那么也许标识符本身也是吗?这样可以简化语法If
no
prefix for negative qualifiers, then maybe the identifier itself is, too? This could simplify the grammarint_
和double_
的顺序使得大多数双精度数在被识别之前都被误解析为int
.考虑更明确的内容,例如x3::strict_real_policies<double>>{} | int_
The ordering of
int_
anddouble_
makes it so that most doubles are mis-parsed asint
before they could ever be recognized. Consider something more explicit likex3::strict_real_policies<double>>{} | int_
如果要解析引用的构造,也许您也想识别转义符(例如,
'\"'
和'\\'
):If you're parsing quoted constructs, perhaps you want to recognize escapes too (
'\"'
and'\\'
for example):auto quoted_string = lexeme['"' >> *('\\' >> char_ | ~char_('"')) >> '"'];
-
如果需要关键字值",请考虑在
x3::symbols<>
中列出已知值.这也可以用于直接解析为枚举类型. If you have a need for "keyword values" consider listing known values in
x3::symbols<>
. This can also be used to parse directly into an enum type.这是一个可解析为AST类型并打印回以供演示的版本:
Here's a version that parses into AST types and prints it back for demonstration purposes:
#include <boost/config/warning_disable.hpp> #include <string> #include <vector> #include <boost/variant.hpp> namespace Ast { struct Keyword : std::string { // needs to be strong-typed to distinguish from quoted values using std::string::string; using std::string::operator=; }; struct Nil {}; using Value = boost::variant<Nil, std::string, int, double, Keyword>; struct Qualifier { enum Kind { positive, negative } kind; std::string identifier; Value value; }; struct Param { Keyword keyword; std::vector<Qualifier> qualifiers; }; struct Command { std::string verb; std::vector<Qualifier> qualifiers; std::vector<Param> params; }; } #include <boost/fusion/adapted/struct.hpp> BOOST_FUSION_ADAPT_STRUCT(Ast::Qualifier, kind, identifier, value) BOOST_FUSION_ADAPT_STRUCT(Ast::Param, keyword, qualifiers) BOOST_FUSION_ADAPT_STRUCT(Ast::Command, verb, qualifiers, params) #include <boost/spirit/home/x3.hpp> namespace x3 = boost::spirit::x3; namespace scl { // // Grammar for simple command language // using x3::char_; using x3::int_; using x3::lexeme; using x3::no_case; // lexeme tokens auto keyword = x3::rule<struct _keyword, Ast::Keyword> { "keyword" } = lexeme [ +char_("a-zA-Z0-9$_.") ]; auto identifier = lexeme [ +char_("a-zA-Z_") ]; auto quoted_string = lexeme['"' >> *('\\' >> x3::char_ | ~x3::char_('"')) >> '"']; auto value = quoted_string | x3::real_parser<double, x3::strict_real_policies<double>>{} | x3::int_ | keyword; auto qual = x3::attr(Ast::Qualifier::positive) >> identifier >> -('=' >> value); auto neg_qual = x3::attr(Ast::Qualifier::negative) >> lexeme[no_case["no"] >> identifier] >> x3::attr(Ast::Nil{}); // never a value auto qualifier = lexeme['/' >> (neg_qual | qual)]; auto verb = identifier; auto parameter = x3::rule<struct _parameter, Ast::Param> {"parameter"} = keyword >> *qualifier; auto command = x3::rule<struct _command, Ast::Command> {"command"} = x3::skip(x3::space) [ verb >> *qualifier >> *parameter ]; } // End namespace scl // For Demo, Debug: printing the Ast types back #include <iostream> #include <iomanip> namespace Ast { static inline std::ostream& operator<<(std::ostream& os, Value const& v) { struct { std::ostream& _os; void operator()(std::string const& s) const { _os << std::quoted(s); } void operator()(int i) const { _os << i; } void operator()(double d) const { _os << d; } void operator()(Keyword const& kwv) const { _os << kwv; } void operator()(Nil) const { } } vis{os}; boost::apply_visitor(vis, v); return os; } static inline std::ostream& operator<<(std::ostream& os, Qualifier const& q) { os << "/" << (q.kind==Qualifier::negative?"no":"") << q.identifier; if (q.value.which()) os << "=" << q.value; return os; } static inline std::ostream& operator<<(std::ostream& os, std::vector<Qualifier> const& qualifiers) { for (auto& qualifier : qualifiers) os << qualifier; return os; } static inline std::ostream& operator<<(std::ostream& os, Param const& p) { return os << p.keyword << p.qualifiers; } static inline std::ostream& operator<<(std::ostream& os, Command const& cmd) { os << cmd.verb << cmd.qualifiers; for (auto& param : cmd.params) os << " " << param; return os; } } int main() { for (std::string const str : { "show/out=\"somefile.txt\" motors/all cameras/full", "start/speed=5 motors arm1 arm2/speed=2.5/track arm3", "rotate camera1/notrack/axis=y/angle=45", }) { auto b = str.begin(), e = str.end(); Ast::Command cmd; bool ok = parse(b, e, scl::command, cmd); std::cout << (ok?"OK":"FAIL") << '\t' << std::quoted(str) << '\n'; if (ok) { std::cout << " -- Full AST: " << cmd << "\n"; std::cout << " -- Verb+Qualifiers: " << cmd.verb << cmd.qualifiers << "\n"; for (auto& param : cmd.params) std::cout << " -- Param+Qualifiers: " << param << "\n"; } if (b != e) { std::cout << " -- Remaining unparsed: " << std::quoted(std::string(b,e)) << "\n"; } } }
打印
OK "show/out=\"somefile.txt\" motors/all cameras/full" -- Full AST: show/out="somefile.txt" motors/all cameras/full -- Verb+Qualifiers: show/out="somefile.txt" -- Param+Qualifiers: motors/all -- Param+Qualifiers: cameras/full OK "start/speed=5 motors arm1 arm2/speed=2.5/track arm3" -- Full AST: start/speed=5 motors arm1 arm2/speed=2.5/track arm3 -- Verb+Qualifiers: start/speed=5 -- Param+Qualifiers: motors -- Param+Qualifiers: arm1 -- Param+Qualifiers: arm2/speed=2.5/track -- Param+Qualifiers: arm3 OK "rotate camera1/notrack/axis=y/angle=45" -- Full AST: rotate camera1/notrack/axis=y/angle=45 -- Verb+Qualifiers: rotate -- Param+Qualifiers: camera1/notrack/axis=y/angle=45
为了完整性
For completeness
- 演示还 Live on MSVC(Rextester)-注意RexTester使用Boost 1.60
- Coliru使用Boost 1.66 ,但问题并没有显现出来,因为现在,解析器绑定了具体的属性值
- Demo also Live On MSVC (Rextester) - note that RexTester uses Boost 1.60
- Coliru uses Boost 1.66 but the problem doesn't manifest itself because now, there are concrete attribute values bound to parsers
这篇关于x3语法中无用的编译器错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!