Boost Spirit模板专业化失败

时间:2017-07-19 12:26:56

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

下面是一个非常紧凑的语法版本我尝试使用 boost :: spirit :: qi 编写。
环境:VS2013,x86,Boost1.64
当#include头文件时,编译器会抱怨该行

rBlock = "{" >> +(rInvocation) >> "}";

有一个很长的日志(我只复制了开头和结尾):

  

多个部分特化与模板参数列表匹配   ...
  ...
   请参阅函数模板实例化的参考   '的boost ::精神::齐::规则   & boost :: spirit :: qi :: rule :: operator =>(const Expr&)'正在编制

我的错误在哪里?

标题文件:

//mygrammar.h
#pragma once 
#include <boost/spirit/include/qi.hpp>

namespace myNS
{

    typedef std::string Identifier;
    typedef ::boost::spirit::qi::rule <const char*, Identifier()> myIdentifierRule;

    typedef ::boost::variant<char, int> Expression;
    typedef ::boost::spirit::qi::rule <const char*, Expression()> myExpressionRule;

    struct IdntifierEqArgument
    {
        Identifier ident;
        Expression arg;
    };

    typedef ::boost::variant < IdntifierEqArgument, Expression >  Argument;
    typedef ::boost::spirit::qi::rule <const char*, Argument()> myArgumentRule;

    typedef ::std::vector<Argument> ArgumentList;
    typedef ::boost::spirit::qi::rule <const char*, myNS::ArgumentList()> myArgumentListRule;


    struct Invocation
    {
        Identifier identifier;
        ::boost::optional<ArgumentList> args;
    };
    typedef ::boost::spirit::qi::rule <const char*, Invocation()> myInvocationRule;


    typedef ::std::vector<Invocation> Block;
    typedef ::boost::spirit::qi::rule <const char*, myNS::Block()> myBlockRule;

}
BOOST_FUSION_ADAPT_STRUCT(
    myNS::IdntifierEqArgument,
    (auto, ident)
    (auto, arg)
    );
BOOST_FUSION_ADAPT_STRUCT(
    myNS::Invocation,
    (auto, identifier)
    (auto, args)
    );

namespace myNS
{
    struct myRules
    {
        myIdentifierRule rIdentifier;
        myExpressionRule rExpression;
        myArgumentRule rArgument;
        myArgumentListRule rArgumentList;
        myInvocationRule rInvocation;
        myBlockRule rBlock;

        myRules()
        {
            using namespace ::boost::spirit;
            using namespace ::boost::spirit::qi;

            rIdentifier = as_string[((qi::alpha | '_') >> *(qi::alnum | '_'))]; 
            rExpression = char_ | int_;
            rArgument = (rIdentifier >> "=" >> rExpression) | rExpression;
            rArgumentList = rArgument >> *("," >> rArgument);
            rInvocation = rIdentifier >> "(" >> -rArgumentList >> ")";
            rBlock = "{" >> +(rInvocation) >> "}";
        }
    };
}

1 个答案:

答案 0 :(得分:1)

我不确定触发问题的位置,但它显然是属性转发规则中含糊不清的一个症状。

  

从概念上讲,这可能是由具有相似/兼容布局的属性类型触发的。在语言理论中,您正在研究C ++的主格类型系统与属性传播系统中结构类型的近似之间的不匹配。但足够的理论主义:)

我不认为attr_cast<>会在这里拯救你,因为它可能会使用相同的机制和启发式方法。

它引起了我的注意,使ArgumentList可选是......不是很有用(因为空列表已经准确地反映了参数的缺失)。

所以我尝试简化规则:

rArgumentList = -(rArgument % ',');
rInvocation   = rIdentifier >> '(' >> rArgumentList >> ')';

声明的属性类型可以只是ArgumentList而不是boost::optional::ArgumentList

这会在传播到vector<Invocation>时消除歧义,所以...你已经保存了。

  

如果这感觉&#34;意外&#34;对你,你应该!如果这没有消除模棱两可的话,我会怎么做?#34 ;?我已经创建了一个语义动作来通过更简单的机制来传播Invocation。 fusion::push_back(_val, _1)或类似的机会很有可能发挥作用。

     

另见Boost Spirit: "Semantic actions are evil"?

回顾与演示

在清理过的评论中,我提供了一些修复/改进和一个转储解析AST的测试运行。

  1. 将AST与解析器分开(您不希望在AST类型中使用qi。您特别不希望using namespace指令面向通用模板库)
  2. 请勿在自适应宏中使用auto。这不是一个功能。相反,既然你可以表面上使用C ++ 11,那么使用基于C ++ 11(decltype)的宏

    BOOST_FUSION_ADAPT_STRUCT(myAST::IdntifierEqArgument, ident,arg);
    BOOST_FUSION_ADAPT_STRUCT(myAST::Invocation, identifier,args);
    
  3. AST领先(同样,为了清晰起见,更喜欢c ++ 11):

    namespace myAST {
        using Identifier = std::string;
        using Expression = boost::variant<char, int>;
    
        struct IdntifierEqArgument {
            Identifier ident;
            Expression arg;
        };
    
        using Argument = boost::variant<IdntifierEqArgument, Expression>;
    
        using ArgumentList = std::vector<Argument>;
    
        struct Invocation {
            Identifier identifier;
            ArgumentList args;
        };
    
        using Block = std::vector<Invocation>;
    }
    

    将定义分开

  4. 真好
  5. 关于解析器,

    • 我更喜欢qi::grammar惯例。此外,
    • 你没有用船长声明任何规则。我猜对了#34;从上下文来看,在ExpressionIdentifier的规则之外,空白是无关紧要的。
    • 表达式每char_个吃一次,所以也会吃')'甚至'3'。我只在测试时和调试后注意到这一点:

      //#define BOOST_SPIRIT_DEBUG
      BOOST_SPIRIT_DEBUG_NODES((start)(rBlock)(rInvocation)(rIdentifier)(rArgumentList)(rArgument)(rExpression))
      

      我强烈建议您使用这些设施

  6. 总而言之,解析器归结为

    namespace myNS {
        namespace qi = boost::spirit::qi;
    
        template <typename Iterator = char const*>
        struct myRules : qi::grammar<Iterator, myAST::Block()> {
    
            myRules() : myRules::base_type(start) {
                rIdentifier   = qi::raw [(qi::alpha | '_') >> *(qi::alnum | '_')];
                rExpression   = qi::alpha | qi::int_;
                rArgument     = (rIdentifier >> '=' >> rExpression) | rExpression;
                rArgumentList = -(rArgument % ',');
                rInvocation   = rIdentifier >> '(' >> rArgumentList >> ')';
                rBlock        = '{' >> +rInvocation >> '}';
                start         = qi::skip(qi::space) [ rBlock ];
    
                BOOST_SPIRIT_DEBUG_NODES((start)(rBlock)(rInvocation)(rIdentifier)(rArgumentList)(rArgument)(rExpression))
            }
    
          private:
            qi::rule<Iterator, myAST::Block()> start;
            using Skipper = qi::space_type;
    
            qi::rule<Iterator, myAST::Argument(), Skipper>     rArgument;
            qi::rule<Iterator, myAST::ArgumentList(), Skipper> rArgumentList;
            qi::rule<Iterator, myAST::Invocation(), Skipper>   rInvocation;
            qi::rule<Iterator, myAST::Block(), Skipper>        rBlock;
            // implicit lexemes
            qi::rule<Iterator, myAST::Identifier()>   rIdentifier;
            qi::rule<Iterator, myAST::Expression()>   rExpression;
        };
    }
    
  7. 添加测试驱动程序

    int main() {
        std::string const input = R"(
    {
        foo()
        bar(a, b, 42)
        qux(someThing_awful01 = 9)
    }
    )";
        auto f = input.data(), l = f + input.size();
    
        myAST::Block block;
        bool ok = parse(f, l, myNS::myRules<>{}, block);
    
        if (ok) {
            std::cout << "Parse success\n";
    
            for (auto& invocation : block) {
                std::cout << invocation.identifier << "(";
                for (auto& arg : invocation.args) std::cout << arg << ",";
                std::cout << ")\n";
            }
        }
        else
            std::cout << "Parse failed\n";
    
        if (f!=l)
            std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
    }
    
  8. 完成演示

    查看 Live On Coliru

    //#define BOOST_SPIRIT_DEBUG
    #include <boost/spirit/include/qi.hpp>
    
    namespace myAST {
        using Identifier = std::string;
        using Expression = boost::variant<char, int>;
    
        struct IdntifierEqArgument {
            Identifier ident;
            Expression arg;
        };
    
        using Argument = boost::variant<IdntifierEqArgument, Expression>;
    
        using ArgumentList = std::vector<Argument>;
    
        struct Invocation {
            Identifier identifier;
            ArgumentList args;
        };
    
        using Block = std::vector<Invocation>;
    
        // for debug printing
        static inline std::ostream& operator<<(std::ostream& os, myAST::IdntifierEqArgument const& named) {
            return os << named.ident << "=" << named.arg;
        }
    }
    
    BOOST_FUSION_ADAPT_STRUCT(myAST::IdntifierEqArgument, ident,arg);
    BOOST_FUSION_ADAPT_STRUCT(myAST::Invocation, identifier,args);
    
    namespace myNS {
        namespace qi = boost::spirit::qi;
    
        template <typename Iterator = char const*>
        struct myRules : qi::grammar<Iterator, myAST::Block()> {
    
            myRules() : myRules::base_type(start) {
                rIdentifier   = qi::raw [(qi::alpha | '_') >> *(qi::alnum | '_')];
                rExpression   = qi::alpha | qi::int_;
                rArgument     = (rIdentifier >> '=' >> rExpression) | rExpression;
                rArgumentList = -(rArgument % ',');
                rInvocation   = rIdentifier >> '(' >> rArgumentList >> ')';
                rBlock        = '{' >> +rInvocation >> '}';
                start         = qi::skip(qi::space) [ rBlock ];
    
                BOOST_SPIRIT_DEBUG_NODES((start)(rBlock)(rInvocation)(rIdentifier)(rArgumentList)(rArgument)(rExpression))
            }
    
          private:
            qi::rule<Iterator, myAST::Block()> start;
            using Skipper = qi::space_type;
    
            qi::rule<Iterator, myAST::Argument(), Skipper>     rArgument;
            qi::rule<Iterator, myAST::ArgumentList(), Skipper> rArgumentList;
            qi::rule<Iterator, myAST::Invocation(), Skipper>   rInvocation;
            qi::rule<Iterator, myAST::Block(), Skipper>        rBlock;
            // implicit lexemes
            qi::rule<Iterator, myAST::Identifier()>   rIdentifier;
            qi::rule<Iterator, myAST::Expression()>   rExpression;
        };
    }
    
    int main() {
        std::string const input = R"(
    {
        foo()
        bar(a, b, 42)
        qux(someThing_awful01 = 9)
    }
    )";
        auto f = input.data(), l = f + input.size();
    
        myAST::Block block;
        bool ok = parse(f, l, myNS::myRules<>{}, block);
    
        if (ok) {
            std::cout << "Parse success\n";
    
            for (auto& invocation : block) {
                std::cout << invocation.identifier << "(";
                for (auto& arg : invocation.args) std::cout << arg << ",";
                std::cout << ")\n";
            }
        }
        else
            std::cout << "Parse failed\n";
    
        if (f!=l)
            std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
    }
    

    打印输出

    Parse success
    foo()
    bar(a,b,42,)
    qux(someThing_awful01=9,)
    Remaining unparsed input: '
    '