2016-07-06 5 views
0

Я начал работать над простым синтаксическим анализатором Boost.Spirit, который будет анализировать C++-подобный файл (единственная часть C++ - ish - это встроенные типы шаблонов; например, map<string, smart_ptr<int>> name_object_map; - но это встроенный компилятор, и пользователь не может объявлять классы шаблонов). Тем не менее, грамматика предназначена для того, чтобы содержать декларации структуры данных, а не выражения, за исключением константных выражений, которые используются для инициализации деклараций перечислителя; enum E { a = 4 * 5 + 3 }; действительно. Это в настоящее время не является проблемой для меня, потому что я не мог разобрать E, как я хочу еще :)не может построить std :: string from placeholder в Boost.Spirit

Я сделал следующее вчера, после прочтения документации и примеров, но он не компилируется:

#include <boost/spirit/include/phoenix.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/qi_char_class.hpp> 
#include <cassert> 
#include <memory> 
#include <string> 
#include <utility> 

struct context {}; 

class foo { 
    std::string name; 
    const context *ctx; 

public: 
    foo(const std::string &name, const context *ctx) : name(name), ctx(ctx) {} 
}; 

using foo_ref = std::shared_ptr<foo>; 

template <typename Iterator> 
struct skipper : boost::spirit::qi::grammar<Iterator> { 
    skipper() : skipper::base_type(start) { 
    using namespace boost::spirit; 
    qi::char_type char_; 
    ascii::space_type space; 

    start = space        // tab/space/cr/lf 
      | "/*" >> *(char_ - "*/") >> "*/" // C-style comments 
     ; 
    } 

    boost::spirit::qi::rule<Iterator> start; 
}; 

template <typename Iterator> 
struct the_parser : boost::spirit::qi::grammar<Iterator, std::vector<foo_ref>(), 
               skipper<Iterator>> { 
    the_parser() : the_parser::base_type(start), current_context(&root) { 
    using namespace boost::spirit; 
    namespace phx = boost::phoenix; 

    identifier = qi::lexeme[qi::alpha >> *qi::alnum]; 
    start = *(foo_decl); // currently, no semantic action attached. 
         // This will create the root decl in ast. 

    foo_decl = (lit("foo") >> identifier)[qi::_val = std::make_shared<foo>(
               qi::_1, current_context)] >> 
       qi::char_('{') >> qi::char_('}') >> qi::char_(';'); 
    BOOST_SPIRIT_DEBUG_NODES((identifier)(start)(foo_decl)); 
    } 
    boost::spirit::qi::rule<Iterator, std::string(), skipper<Iterator>> 
     identifier; 
    boost::spirit::qi::rule<Iterator, std::vector<foo_ref>(), skipper<Iterator>> 
     start; 
    boost::spirit::qi::rule<Iterator, foo_ref(), skipper<Iterator>> foo_decl; 
    context root; 
    const context *current_context; 
}; 

int main() { 
    the_parser<std::string::const_iterator> parser; 
    std::vector<foo_ref> root; 

    const std::string content = "foo john_doe { };"; 
    auto first = content.cbegin(), last = content.cend(); 
    bool r = boost::spirit::qi::phrase_parse(
     first, last, parser, skipper<std::string::const_iterator>(), root); 
    assert(r && first == last); 
} 

Компиляция это с грохотом на Mac (первая линия std::make_shared):

error: no matching constructor for initialization of 'foo' 
      __second_(_VSTD::forward<_Args2>(_VSTD::get<_I2>(__second_args))...) 
     ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
... 
note: candidate constructor not viable: no known conversion from 'const boost::phoenix::actor<boost::spirit::argument<0> >' to 'const std::string' (aka 
    'const basic_string<char, char_traits<char>, allocator<char> >') for 1st argument 
    foo(const std::string &name, const context *ctx) : name(name), ctx(ctx) {} 
^

В foo_decl сек семантического действия, он не может построить foo (через std::make_shared), becaus e результат первого атрибута не может быть преобразован в std::string. Однако, если добавить класс члена std::string s, и сделать это вместо того, она работает:

foo_decl = (lit("foo") >> identifier)[boost::phoenix::ref(s) = qi::_1] >> 
       qi::char_('{') >> qi::char_('}') >> qi::char_(';'); 

Точно так же, если я пытаюсь std::cout, я могу видеть john_doe распечатывается.

Если я связать вызов функции-члена с фениксом, он также работает:

foo_decl = (lit("foo") >> identifier)[qi::_val = 
        boost::phoenix::bind(&the_parser, this, qi::_1)] >> 
      qi::char_('{') >> qi::char_('}') >> qi::char_(';'); 

foo_ref make_foo(const std::string &n) { 
    return std::make_shared(n, current_context); 
} 

Эти последние три обходные означает, что существует неявное преобразование из последовательности decltype(qi::_1) в std::string; разве это не так?

Я был бы очень рад, если бы вы могли показать мне свою ошибку или пробел в моем понимании того, как работают семантические действия и заполнители. Мне очень странно, почему std::make_shared не работает.

Спасибо!

ответ

2

Прежде всего:

Используя феникса :: функция из первой ссылки:

Live On Coliru

#define BOOST_SPIRIT_DEBUG 
#include <boost/spirit/include/phoenix.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <cassert> 
#include <memory> 
#include <string> 
#include <utility> 

namespace { 
    template <typename T> struct make_shared_f { 
     template <typename... A> struct result { typedef std::shared_ptr<T> type; }; 

     template <typename... A> typename result<A...>::type operator()(A &&... a) const { 
      return std::make_shared<T>(std::forward<A>(a)...); 
     } 
    }; 

    template <typename T> using make_shared_ = boost::phoenix::function<make_shared_f<T> >; 
} 

struct context {}; 

class foo { 
    std::string name; 
    const context *ctx; 

    public: 
    foo(const std::string &name, const context *ctx) : name(name), ctx(ctx) {} 
}; 

using foo_ref = std::shared_ptr<foo>; 

template <typename Iterator> struct skipper : boost::spirit::qi::grammar<Iterator> { 
    skipper() : skipper::base_type(start) { 
     using namespace boost::spirit; 
     qi::char_type char_; 
     ascii::space_type space; 

     start = space        // tab/space/cr/lf 
       | "/*" >> *(char_ - "*/") >> "*/" // C-style comments 
      ; 
    } 

    boost::spirit::qi::rule<Iterator> start; 
}; 

template <typename Iterator> 
struct the_parser : boost::spirit::qi::grammar<Iterator, std::vector<foo_ref>(), skipper<Iterator> > { 
    the_parser() : the_parser::base_type(start), current_context(&root) { 
     using namespace boost::spirit; 
     namespace phx = boost::phoenix; 

     identifier = qi::alpha >> *qi::alnum; 
     // This will create the root decl in ast. 

     foo_decl = ("foo" >> identifier) [qi::_val = make_shared_<foo>{}(qi::_1, current_context)] >> 
        '{' >> '}' >> ';'; 

     start  = *foo_decl; // currently, no semantic action attached. 
     BOOST_SPIRIT_DEBUG_NODES((identifier)(start)(foo_decl)); 
    } 
    boost::spirit::qi::rule<Iterator, std::string()> identifier; 
    boost::spirit::qi::rule<Iterator, foo_ref(), skipper<Iterator> > foo_decl; 
    boost::spirit::qi::rule<Iterator, std::vector<foo_ref>(), skipper<Iterator> > start; 
    context root; 
    const context *current_context; 
}; 

int main() { 
    the_parser<std::string::const_iterator> parser; 
    std::vector<foo_ref> root; 

    const std::string content = "foo johndoe { };"; 
    auto first = content.cbegin(), last = content.cend(); 
    bool r = boost::spirit::qi::phrase_parse(first, last, parser, skipper<std::string::const_iterator>(), root); 
    if (r) 
     std::cout << "success\n"; 
    else 
     std::cout << "failed\n"; 

    if (first != last) 
     std::cout << "remaining unparsed: '" << std::string(first,last) << "'\n"; 

} 

Печать

success 

Наряду с выводом отладочной

<start> 
    <try>foo johndoe { };</try> 
    <foo_decl> 
    <try>foo johndoe { };</try> 
    <identifier> 
     <try>johndoe { };</try> 
     <success> { };</success> 
     <attributes>[[j, o, h, n, d, o, e]]</attributes> 
    </identifier> 
    <success></success> 
    <attributes>[0x60600000ebb0]</attributes> 
    </foo_decl> 
    <foo_decl> 
    <try></try> 
    <fail/> 
    </foo_decl> 
    <success></success> 
    <attributes>[[0x60600000ebb0]]</attributes> 
</start> 
+0

Я вернусь позже, чтобы посмотреть на ваш конкретный пример. – sehe

+0

Спасибо! Я думаю, что могу жить без 'shared_ptr', но я не понимал, как я могу обойтись без семантических действий. После синтаксического анализа идентификатор, как я могу построить 'foo_decl' с совпадающим идентификатором? – user1150609

+0

Я обновил демо с вашим конкретным образцом: [Live on Coliru] (http://coliru.stacked-crooked.com/a/c69eaa18dd6a6f40). W.r.t. указатели «parent context», похоже, вы просто хотите построить рекурсивное дерево AST. У меня есть, по крайней мере, несколько десятков примеров того, что происходит на [SO], но я не вижу, что вам нужно для данной грамматики. – sehe