x3语法中无用的编译器错误

时间:2018-03-04 20:13:19

标签: c++ boost boost-spirit boost-spirit-x3

以下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

1 个答案:

答案 0 :(得分:2)

自Boost 1.65以来有一个回归导致一些可能传播到容器类型属性的规则出现问题。

在没有实际绑定属性的情况下实例化时,它们会调度到错误的重载。发生这种情况时,会出现名为unused_type的“模拟”属性类型。您看到的错误表明unused_type被视为具体的属性类型,显然不会飞。

回归在https://github.com/boostorg/spirit/commit/ee4943d5891bdae0706fb616b908e3bf528e0dfa

中得到修正

通过使用Boost 1.64进行编译,您可以看到它是一个回归:

现在,最新版本应该修复它,但您只需复制修补后的文件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[]作为动词)

    < / LI>
  • 如果"one two three"前缀为负限定词,那么也许标识符本身也是?这可以简化语法

  • noint_的排序使得大多数双打在被识别之前被误解为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 问题并没有表现出来,因为现在,有一些具体属性值绑定到解析器
  •