Получение AST из boost :: spirit parser - PullRequest
8 голосов
/ 11 декабря 2011

После того, как я прочитал руководства по boost :: spirit , мне это очень понравилось из-за синтаксиса синтаксического анализатора. Создать парсер очень просто.

К сожалению, учебники были не такими точными в том, что касается извлечения сложной структуры данных из анализатора. Я пытаюсь добраться до Калейдоскоп АСТ .

В любом случае, вот мой код AST:

#ifndef __AST_HPP__
#define __AST_HPP__

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <string>
#include <vector>

namespace ast {

struct add;
struct sub;
struct mul;
struct div;
struct func_call;
template<typename OpTag> struct binary_op;

typedef boost::variant<double, std::string, boost::recursive_wrapper<binary_op<
        add>>, boost::recursive_wrapper<binary_op<sub>>,
        boost::recursive_wrapper<binary_op<mul>>, boost::recursive_wrapper<
                binary_op<div>>, boost::recursive_wrapper<func_call>>
        expression;

template<typename OpTag>
struct binary_op {
    expression left;
    expression right;

    binary_op(const expression & lhs, const expression & rhs) :
        left(lhs), right(rhs) {
    }
};

struct func_call {
    std::string callee;
    std::vector<expression> args;

    func_call(const std::string func, const std::vector<expression> &args) :
        callee(func), args(args) {
    }
};

struct prototype {
    std::string name;
    std::vector<std::string> args;

    prototype(const std::string &name, const std::vector<std::string> &args) :
        name(name), args(args) {
    }
};

struct function {
    prototype proto;
    expression body;

    function(const prototype &proto, const expression &body) :
        body(body), proto(proto) {
    }
};

}
    #endif

Я опустил BOOST_FUSION_ADAPT_STRUCT части, но они есть.

А это мой синтаксический анализатор выражений:

#ifndef __PARSER_HPP__
#define __PARSER_HPP__

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include "ast.hpp"

namespace parser {

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

template<typename Iterator>
struct expression: qi::grammar<Iterator, ast::expression(), ascii::space_type> {
    expression() :
        expression::base_type(expr) {
        using qi::lit;
        using qi::lexeme;
        using ascii::char_;
        using ascii::string;
        using ascii::alnum;
        using ascii::alpha;
        using qi::double_;
        using namespace qi::labels;

        using phoenix::at_c;
        using phoenix::push_back;

        number %= lexeme[double_];
        varname %= lexeme[alpha >> *(alnum | '_')];

        binop
                = (expr >> '+' >> expr)[_val = ast::binary_op<ast::add>(_1, _3)]
                        | (expr >> '-' >> expr)[_val
                                = ast::binary_op<ast::sub>(_1, _3)]
                        | (expr >> '*' >> expr)[_val
                                = ast::binary_op<ast::mul>(_1, _3)]
                        | (expr >> '/' >> expr)[_val
                                = ast::binary_op<ast::div>(_1, _3)];

        expr %= number | varname | binop;
    }

    qi::rule<Iterator, ast::expression(), ascii::space_type> expr;
    qi::rule<Iterator, ast::expression(), ascii::space_type> binop;
    qi::rule<Iterator, std::string, ascii::space_type> varname;
    qi::rule<Iterator, double, ascii::space_type> number;
};

}

#endif

Проблема, с которой я столкнулся, заключается в том, что, похоже, возникла проблема с результатом ast::expression. Скомпилированный выбрасывает более 200 строк сложных ошибок шаблона. Я подозреваю, что это связано с тем, как я пытался получить информацию из правила binop, но я не уверен.

Может кто-нибудь помочь?

1 Ответ

7 голосов
/ 11 декабря 2011

Вы пытаетесь вызвать конструктор ast::binary_op, используя заполнители Boost Phoenix.Они плохо смешиваются.Вам нужно использовать ленивый вызов для конструктора ast::binary_op.Это обеспечивается в Фениксе с помощью construct:

binop = (expr >> '+' >> expr) [_val = construct< ast::binary_op<ast::add> >(_1, _2)]
      | (expr >> '-' >> expr) [_val = construct< ast::binary_op<ast::sub> >(_1, _2)]
      | (expr >> '*' >> expr) [_val = construct< ast::binary_op<ast::mul> >(_1, _2)]
      | (expr >> '/' >> expr) [_val = construct< ast::binary_op<ast::div> >(_1, _2)] ;

Кроме того, я думаю, что вам нужны только заполнители _1 и _2, такие как '+', '-', ...преобразуются в qi::lit (литеральный), таким образом, не имеют атрибутов.

Я также отметил пару пропущенных скобок в правилах varname и number:

qi::rule<Iterator, std::string(), ascii::space_type> varname;
//                            ^^          
qi::rule<Iterator, double(), ascii::space_type> number;
//                       ^^

Boost Spirit Qiочень мощный, но очень сложный для отладки.Когда я начал его использовать, я обнаружил, что Приложения для повышения духа очень полезны.

Надеюсь, это будет полезно, так как я не эксперт в области повышения духа.

...