如何在带有 Boost Spirit 的 AST 中使用只有一个属性的类?

Posted

技术标签:

【中文标题】如何在带有 Boost Spirit 的 AST 中使用只有一个属性的类?【英文标题】:How do I use a class with only one attribute in a AST with Boost Spirit? 【发布时间】:2011-10-30 19:18:58 【问题描述】:

我想使用 Boost Spirit 将文件解析为 AST。

我的 AST 的根是一个只有一个属性的类:

typedef boost::variant<FunctionDeclaration, GlobalVariableDeclaration> FirstLevelBlock;

struct Program 
    std::vector<FirstLevelBlock> blocks;
;

BOOST_FUSION_ADAPT_STRUCT(
    ::Program,
    (std::vector<eddic::FirstLevelBlock>, blocks)
)

如果我使用单个规则进行解析:

program %= *(function | globalDeclaration);

它不会编译,但如果我向 Program 添加一个字符串名称,它会运行良好。我可以使用向量作为根,但我想使用类,因为我想向 Program 类添加一些方法。

编辑:

如果我用大括号包围我的程序,它会很好地工作:

program %= lexer.left_brace >> *(function | globalDeclaration) >> lexer.right_brace;

编译和工作正常,但是:

program %= *(function | globalDeclaration);

无法编译...

Boost Spirit 中有什么东西可以阻止使用这种简单的规则吗?

【问题讨论】:

【参考方案1】:

已编辑问题version 2

如果我用大括号括住我的程序,它会运行良好 [...],但 program %= *(function | globalDeclaration); 无法编译...

Boost Spirit 中是否有一些东西阻止使用如此简单的规则?

首先,如果没有functionglobalDeclaration 的定义,我们真的无法分辨。

第二次我尝试,将我的 PoC 线路更改为

static const qi::rule<It, Program(), space_type> program  = *(function | global);
Program d = test("void test(); int abc; int xyz; void last();" , program); 

瞧,我得到了你的编译器错误!现在我当然同意这看起来非常像一个属性转换错误。另外,这是一个初步的解决方法:

program %= eps >> *(function | global);

如你所见,qi::eps 来救援


回答原问题version 1

嗯。我认为您需要发布一个最小的工作样本。这是从您的问题开始的概念证明,并且效果很好。

请注意,我使用 g++ -std=c++0x 编译是为了在 test 函数上获得默认的 Attr 参数参数。

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/strong_typedef.hpp>

// added missing bits
namespace eddic 

    typedef std::string FunctionDeclaration;
    typedef std::string GlobalVariableDeclaration;

    typedef boost::variant<FunctionDeclaration, GlobalVariableDeclaration> FirstLevelBlock;


using namespace eddic;
// end missing bits

struct Program 
    std::vector<FirstLevelBlock> blocks;
;

BOOST_FUSION_ADAPT_STRUCT(
    ::Program,
    (std::vector<eddic::FirstLevelBlock>, blocks)
)

namespace /*anon*/
    
    using namespace boost::spirit::karma;

    struct dumpvariant : boost::static_visitor<std::ostream&>
    
        dumpvariant(std::ostream& os) : _os(os) 
        template <typename T> std::ostream& operator ()(const T& t) const
             return _os << format(stream, t); 

      private: std::ostream& _os;
    ;

    std::ostream& operator<<(std::ostream& os, const FirstLevelBlock& block)
     
        os << "variant[" << block.which() << ", ";
        boost::apply_visitor(dumpvariant(os), block);
        return os << "]";
    

    std::ostream& operator<<(std::ostream& os, const std::vector<FirstLevelBlock>& blocks)
     return os << format(-(stream % eol), blocks); 

    std::ostream& operator<<(std::ostream& os, const Program& program)
     return os << "BEGIN\n" << program.blocks << "\nEND"; 


namespace qi = boost::spirit::qi;

template <typename Rule, typename Attr = typename Rule::attr_type>
    Attr test(const std::string& input, const Rule& rule)

    typedef std::string::const_iterator It;
    It f(input.begin()), l(input.end());

    Attr result;
    try
    
        bool ok = qi::phrase_parse(f, l, rule, qi::space, result);
        if (!ok)
            std::cerr << " -- ERR: parse failed" << std::endl;
     catch(qi::expectation_failure<It>& e)
    
        std::cerr << " -- ERR: expectation failure at '" << std::string(e.first, e.last) << "'" << std::endl;
    
    if (f!=l)
        std::cerr << " -- WARN: remaing input '" << std::string(f,l) << "'" << std::endl;
    return result;


int main()

    typedef std::string::const_iterator It;
    static const qi::rule<It, FunctionDeclaration(), space_type>        function = "void " > +~qi::char_("()") > "();";
    static const qi::rule<It, GlobalVariableDeclaration(), space_type>  global   = "int "  > +~qi::char_(";")  > ";";
    static const qi::rule<It, FirstLevelBlock(), space_type>            block    = function | global;
    static const qi::rule<It, Program(), space_type>                    program  = '' >> *(function | global) >> '';

    FunctionDeclaration       a = test("void test();", function); 
    std::cout << "FunctionDeclaration a         : " << a << std::endl;

    GlobalVariableDeclaration b = test("int abc;", global); 
    std::cout << "GlobalVariableDeclaration b   : " << b << std::endl;

    FirstLevelBlock c = test("void more();", block); 
    std::cout << "FirstLevelBlock c             : " << c << std::endl;

    /*FirstLevelBlock*/ c = test("int bcd;", block); 
    std::cout << "FirstLevelBlock c             : " << c << std::endl;

    Program d = test(""
            "void test();"
            "int abc"
            ";"
            "int xyz; void last();"
            "", program); 
    std::cout << "Program d                     : " << d << std::endl;

输出:

FunctionDeclaration a         : test
GlobalVariableDeclaration b   : abc
FirstLevelBlock c             : variant[1, more]
FirstLevelBlock c             : variant[1, bcd]
Program d                     : BEGIN
test
abc
xyz
last
END

【讨论】:

我现在没有测试你的代码,我明天再做,但我有一个小问题:为什么它在没有自动规则 (%=) 的情况下工作,我认为没有自动规则,您必须使用语义操作来构建 AST。 @BaptisteWicht:几乎正确:对于语义操作,您必须使用 %= 才能仍然获得自动属性。如果没有语义动作,规则总是使用自动属性传播(否则像qi::int_ 这样的子表达式甚至不会在没有语义动作的情况下做任何事情) @BaptisteWicht:我复制了它并提供了一种解决方法。希望有帮助

以上是关于如何在带有 Boost Spirit 的 AST 中使用只有一个属性的类?的主要内容,如果未能解决你的问题,请参考以下文章

boost::spirit 算术公式解析器无法编译

Boost::Spirit 表达式解析器,带有定义的函数

Boost Spirit x3 -- 使用其他解析器参数化解析器

约束现有的 Boost.Spirit real_parser(带有策略)

使用 boost Spirit 解析带有二进制信封的文本文件

Boost.Spirit 的单元测试