以下Spirit x3语法用于简单的机器人命令语言在Windows Visual Studio 17中生成编译器错误。对于此项目,我需要使用警告级别编译为4(/ W4)并将警告视为错误(/ WX )。
警告C4127条件表达式是 恒定SpiritTest e:\ data \ boost \ boost_1_65_1 \ boost \ spirit \ home \ x3 \ char \ detail \ cast_char.hpp 29
错误C2039'insert':不是。的成员 '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'结束':不是 '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'空':不是 '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'start':不是 '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星(*参数只是参数),错误就会消失,但之后会收到很多这样的警告:
警告C4459'digit'声明隐藏全局 声明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
#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
答案 0 :(得分:2)
自Boost 1.65以来有一个回归导致一些可能传播到容器类型属性的规则出现问题。
在没有实际绑定属性的情况下实例化时,它们会调度到错误的重载。发生这种情况时,会出现名为unused_type
的“模拟”属性类型。您看到的错误表明unused_type
被视为具体的属性类型,显然不会飞。
回归在https://github.com/boostorg/spirit/commit/ee4943d5891bdae0706fb616b908e3bf528e0dfa
中得到修正通过使用Boost 1.64进行编译,您可以看到它是一个回归:
Boost 1.64编译罚款 GCC and Clang
现在,最新版本应该修复它,但您只需复制修补后的文件even just the 7-line patch。
当我链接重复的问题How to make a recursive rule in boost spirit x3 in VS2017时,以上所有内容都已经可用,这会突出显示相同的回归
using namespace std; // Must be after Boost stuff!
实际上,除非在本地范围内,否则它可能无处可去,您可以在其中看到任何潜在名称分类的影响。
考虑封装船长,因为它可能在逻辑上是你的语法规范的一部分,而不是被调用者覆盖的东西。
这是一个错误:
auto quoted_string = '"' >> *(lexeme[~char_('"')]) >> '"';
你可能想要断言整个文字是lexeme,而不是单个字符(那是......因为空格永远不会打到解析器,因为船长)。
auto quoted_string = lexeme['"' >> *~char_('"') >> '"'];
同样,您可能打算将+keyword_value_chars
作为词汇,因为现在one=two three four
将使用{{1}的“关键字值”解析“限定符”one
},而不是onethreefour
¹
one three four
跳过嵌入的换行符,如果不是意图,请使用x3::space
由于PEG语法从左到右解析 greedy ,因此您可以订购x3::blank
作品而不使用qualifier
前瞻断言。这不仅可以消除重复,还可以使语法更简单,更有效:
!(no_case["no"])
¹注意(Post-Scriptum)现在我们注意到
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[]
运算符周围的空格(当前,它不是),或者是否可以用空格分隔限定符(如=
;目前他们可以)。
也许id /a /b
也需要一些verb
(除非你真的 想要解析lexemes[]
作为动词)
如果"one two three"
前缀为负限定词,那么也许标识符本身也是?这可以简化语法
no
和int_
的排序使得大多数双打在被识别之前被误解为double_
。考虑更明确的内容,例如int
如果您正在解析引用的构造,也许您也想识别转义(例如x3::strict_real_policies<double>>{} | int_
和'\"'
):
'\\'
如果您需要“关键字值”,请考虑在auto quoted_string = lexeme['"' >> *('\\' >> char_ | ~char_('"')) >> '"'];
中列出已知值。这也可以用于直接解析为枚举类型。
这是一个解析为AST类型的版本,并将其打印出来用于演示目的:
<强> Live On Coliru 强>
x3::symbols<>
打印
#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";
}
}
}
完整性
- 演示Live On MSVC (Rextester) - 请注意RexTester使用Boost 1.60
- Coliru使用Boost 1.66 但问题并没有表现出来,因为现在,有一些具体属性值绑定到解析器