Виртуальные классы как узлы AST с Духом - PullRequest
0 голосов
/ 15 ноября 2018

Я работал над переводчиком для языка с другом, и мы начали с решения, которое, как мне кажется, было не слишком мудрым: сначала мы сделали все элементы для выполнения (практически дерево, сделанное из разных классов); но теперь, глядя на примеры повышения, я запутался в том, как объединить их. Я знаю, с чего начать (грамматика), я знаю, чего достичь (экземпляры классов владеют друг другом), я не знаю, как этого достичь.

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

Пример элементов выражения:

namespace exp
{
class op
    {
    private:
    public:
        virtual double exec(function_scope &fs);

    };

class operand : public op
    {
    private:
        double value;

    public:
        operand(double value);
        double exec(function_scope &fs);
    };

class op_bin : public op
    {
    private:
    public:
        op * ll;
        op* rr;
        op_bin(op* ll, op* rr);
        ~op_bin();
    };

namespace bin
    {
    class sum : public op_bin
        {
        public:
            sum(op* ll, op* rr);
            double exec(function_scope &fs);
        };
    }
}

Игнорировать функцию exec, она используется во время выполнения.

Например, код 5 + (2 + 1) должен привести к окончательному эквиваленту:

new exp::bin::sum(new exp::operand(5), new exp::bin::sum(new exp::operand(2), new exp::operand(1))

Как только я понял, как это сделать, я практически сделал.

1 Ответ

0 голосов
/ 16 ноября 2018

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

Несколько ключевых точек:

  • Я немного изменил, переименовал и расширил ваш ast, чтобы он работал и действительно что-то показывал.
  • Духовные правила по какой-то причине делают копию атрибута (я думаю, что это ошибка), поэтому я обошелэта проблема для unique_ptr с чертой.
  • Я не уверен, действительно ли там требуется x3::omit (вы можете удалить все, кроме последнего, и он скомпилируется), но похоже, что это другойошибка в Духе.
  • make_node выглядит ненадежно и может неожиданно оборваться, вы можете разбить ее на отдельных создателей унарных / двоичных узлов, если хотите.
  • В какой-то момент вы захотитеиспользуйте распределитель состояний с сохранением для создания ваших аст-узлов, это должно быть очень просто, вставив распределитель в контекст синтаксического анализатора.Я оставляю это для вас в качестве упражнения.

Парсер:

#include <boost/spirit/home/x3.hpp>
#include <memory>
#include <iostream>

namespace ast
{

class expression
{
protected:
    expression() = default;
public:
    virtual ~expression() = default;
    expression(expression&& other) = delete;
    expression& operator=(expression&& other) = delete;

    virtual void print(std::ostream&) const = 0;

    friend std::ostream& operator<<(std::ostream& os, expression const& node)
    {
        node.print(os);
        return os;
    }
};

class operand : public expression
{
    double value_;

public:
    constexpr operand(double value) : value_{value} {}
    void print(std::ostream& os) const override { os << value_; }
};

class op_bin : public expression
{
protected:
    std::unique_ptr<expression> left_, right_;

public:
    op_bin(std::unique_ptr<expression> left, std::unique_ptr<expression> right)
      : left_{ std::move(left) }, right_{ std::move(right) }
    {}

    op_bin(expression * left, expression * right)
        : left_{ left }, right_{ right }
    {}
};

class plus : public op_bin
{
public:
    using op_bin::op_bin;
    void print(std::ostream& os) const override
    { os << '(' << *left_ << " + " << *right_ << ')'; }
};

class minus : public op_bin
{
public:
    using op_bin::op_bin;
    void print(std::ostream& os) const override
    { os << '(' << *left_ << " - " << *right_ << ')'; }
};

class mul : public op_bin
{
public:
    using op_bin::op_bin;
    void print(std::ostream& os) const override
    { os << '(' << *left_ << " * " << *right_ << ')'; }
};

class div : public op_bin
{
public:
    using op_bin::op_bin;
    void print(std::ostream& os) const override
    { os << '(' << *left_ << " / " << *right_ << ')'; }
};

} // namespace ast

namespace grammar
{

namespace x3 = boost::spirit::x3;

template <typename T>
struct make_node_
{
    template <typename Context>
    void operator()(Context const& ctx) const
    {
        if constexpr (std::is_convertible_v<decltype(x3::_attr(ctx)), T>) {
            x3::_val(ctx) = std::make_unique<T>(std::move(x3::_attr(ctx)));
        }
        else {
            x3::_val(ctx) = std::make_unique<T>(std::move(x3::_val(ctx)), std::move(x3::_attr(ctx)));
        }
    }
};

template <typename T>
constexpr make_node_<T> make_node{};

using x3::double_;
using x3::char_;

x3::rule<class expression_r, std::unique_ptr<ast::expression>, true> const expression;
x3::rule<class prec1_r, std::unique_ptr<ast::expression>, true> const prec1;
x3::rule<class prec0_r, std::unique_ptr<ast::expression>, true> const prec0;

auto const expression_def =
    prec1
    >> *(   x3::omit[('+' > prec1)[make_node<ast::plus>]]
        |   x3::omit[('-' > prec1)[make_node<ast::minus>]]
        )
    ;

auto const prec1_def =
    prec0
    >> *(   x3::omit[('*' > prec0)[make_node<ast::mul>]]
        |   x3::omit[('/' > prec0)[make_node<ast::div>]]
        )
    ;

auto const prec0_def =
        x3::omit[double_[make_node<ast::operand>]]
    |   '(' > expression > ')'
    ;

BOOST_SPIRIT_DEFINE(
    expression
  , prec1
  , prec0
);

} // namespace grammar

namespace boost::spirit::x3::traits {

template <typename Attribute>
struct make_attribute<std::unique_ptr<Attribute>, std::unique_ptr<Attribute>>
  : make_attribute_base<std::unique_ptr<Attribute>>
{
    typedef std::unique_ptr<Attribute>& type;
    typedef std::unique_ptr<Attribute>& value_type;
};

} // namespace grammar

int main()
{
    namespace x3 = boost::spirit::x3;

    std::string s = "1 + 2 * (3 - 4) / 5";
    std::unique_ptr<ast::expression> expr;
    if (auto iter = s.cbegin(); !phrase_parse(iter, s.cend(), grammar::expression, x3::space, expr)) {
        std::cout << "parsing failed";
    }
    else {
        if (iter != s.cend())
            std::cout << "partially parsed\n";
        std::cout << *expr << '\n';
    }
}

Вывод:

(1 + ((2 * (3 - 4)) / 5))
...