Как использовать класс только с одним атрибутом в AST с Boost Spirit?

Я хочу преобразовать файл в AST с помощью Boost Spirit.

Корень моего 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 %= lexer.left_brace >> *(function | globalDeclaration) >> lexer.right_brace;

компилируется и работает нормально, но:

program %= *(function | globalDeclaration);

не компилируется...

Есть ли в Boost Spirit что-то, что мешает использовать такие простые правила?


person Baptiste Wicht    schedule 30.10.2011    source источник


Ответы (1)


Отредактированный вопрос версия 2

Если я окружу свою программу фигурными скобками, она работает хорошо [...], но program %= *(function | globalDeclaration); не компилируется...

Есть ли в Boost Spirit что-то, что мешает использовать такие простые правила?

Во-первых, мы не можем ничего сказать без определения function и globalDeclaration.

Во-вторых, я попытался, изменив свои строки 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 спешит на помощь


Ответ на исходный вопрос версия 1

М-м-м. Я думаю, вам нужно разместить минимальный рабочий образец. Вот доказательство концепции, начиная с вашего вопроса, и все это работает довольно хорошо.

Обратите внимание, что я скомпилировал с g++ -std=c++0x, чтобы получить аргумент параметра Attr по умолчанию для функции test.

#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

person sehe    schedule 30.10.2011
comment
Я не тестировал ваш код сейчас, я сделаю это завтра, но у меня есть небольшой вопрос: почему он работает без автоправила (%=), я думал, что без автоправил вам нужно использовать семантические действия построить АСТ. - person Baptiste Wicht; 31.10.2011
comment
@BaptisteWicht: почти правильно: с семантическими действиями вы должны использовать %=, чтобы по-прежнему получать автоматические атрибуты. Без семантических действий правила всегда используют автоматическое распространение атрибутов (иначе такие подвыражения, как qi::int_, даже ничего не делали бы без семантических действий) - person sehe; 31.10.2011
comment
@BaptisteWicht: я воспроизвел это и также предложил обходной путь. надеюсь, это поможет - person sehe; 31.10.2011