Я начал работать над простым синтаксическим анализатором 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
не работает.
Спасибо!
Я вернусь позже, чтобы посмотреть на ваш конкретный пример. – sehe
Спасибо! Я думаю, что могу жить без 'shared_ptr', но я не понимал, как я могу обойтись без семантических действий. После синтаксического анализа идентификатор, как я могу построить 'foo_decl' с совпадающим идентификатором? – user1150609
Я обновил демо с вашим конкретным образцом: [Live on Coliru] (http://coliru.stacked-crooked.com/a/c69eaa18dd6a6f40). W.r.t. указатели «parent context», похоже, вы просто хотите построить рекурсивное дерево AST. У меня есть, по крайней мере, несколько десятков примеров того, что происходит на [SO], но я не вижу, что вам нужно для данной грамматики. – sehe