Ответ 1
Я сделал некоторые хаки здесь и реорганизовал вещи a, чтобы показать не-runtime-полиморфный стиль:
- https://bitbucket.org/sehe/joos2compiler-refactor (на основе вашей фиксации 12d01e5).
Я надеюсь, что это не увеличит время компиляции:) (Я не собирался разделить грамматику, но она уменьшилась).
Особенности:
- больше кучи не выделяют узлы AST (даже для деревьев, таких как
expression
и/илиstatement
); следовательно, более явное клонирование и/или ложные константные члены. -
Я заменил Maybe.hpp
#pragma once #include <boost/optional.hpp> template <typename T> using Maybe = boost::optional<T>;
Это быстро и грязно, но все это компилируется
-
Я заменил open type-change своими собственными незначительными усилиями (я не мог заставить его работать, а также с форсированным вариантом, все это встроено):
namespace visitor_galore // this is my make-shift replacement for typeswitch (I couldn't find it/make it work) { template<typename T, class...Fs> struct visitor_t; template<typename T, class F1, class...Fs> struct visitor_t<T, F1, Fs...> : F1, visitor_t<T, Fs...>::type { typedef visitor_t type; visitor_t(F1 head, Fs...tail) : F1(head), visitor_t<T, Fs...>::type(tail...) {} using F1::operator(); using visitor_t<T, Fs...>::type::operator(); }; template<typename T, class F> struct visitor_t<T, F> : F, boost::static_visitor<T> { typedef visitor_t type; visitor_t(F f) : F(f) {} using F::operator(); }; template<typename T=void, class...Fs> typename visitor_t<T, Fs...>::type make_visitor(Fs...x) { return {x...}; } } using visitor_galore::make_visitor;
Чтобы узнать, как это используется, посмотрите на, например,
ast_pp.cpp
:void pretty_print(expression_incdec const& exp) { boost::apply_visitor( make_visitor( [&exp](inc_dec_op_preinc const& op) { std::cout << "++"; pretty_print(exp.variable); }, [&exp](inc_dec_op_predec const& op) { std::cout << "--"; pretty_print(exp.variable); }, [&exp](inc_dec_op_postinc const& op) { pretty_print(exp.variable); std::cout << "++"; }, [&exp](inc_dec_op_postdec const& op) { pretty_print(exp.variable); std::cout << "--"; } ) , exp.operatur); }
БОНУС Если вам не все равно, чтобы перечислять все типы в ветвях, например. потому что все они по умолчанию вызывают одну и ту же свободную функцию (или перегрузки), вы можете использовать полиморфного посетителя:
static const struct pretty_print_visitor_ : boost::static_visitor<> { template<typename T> void operator ()(T const& v) const { pretty_print(v); } } pretty_print_visitor;
например. теперь вы можете заменить 24 ветки на
expression&
:boost::apply_visitor( make_visitor( [](expression_binop const& exp) { pretty_print(exp); }, [](expression_unop const& exp) { pretty_print(exp); }, [](expression_integer_constant const& exp) { pretty_print(exp); }, [](expression_character_constant const& exp) { pretty_print(exp); }, [](expression_string_constant const& exp) { pretty_print(exp); }, [](expression_boolean_constant const& exp) { pretty_print(exp); }, [](expression_null const& exp) { pretty_print(exp); }, [](expression_this const& exp) { pretty_print(exp); }, [](expression_static_invoke const& exp) { pretty_print(exp); }, [](expression_non_static_invoke const& exp) { pretty_print(exp); }, [](expression_simple_invoke const& exp) { pretty_print(exp); }, [](expression_ambiguous_invoke const& exp) { pretty_print(exp); }, [](expression_new const& exp) { pretty_print(exp); }, [](expression_new_array const& exp) { pretty_print(exp); }, [](expression_lvalue const& exp) { pretty_print(exp); }, [](expression_assignment const& exp) { pretty_print(exp); }, [](expression_incdec const& exp) { pretty_print(exp); }, [](expression_cast const& exp) { pretty_print(exp); }, [](expression_ambiguous_cast const& exp) { pretty_print(exp); }, [](expression_instance_of const& exp) { pretty_print(exp); }, [](expression_parentheses const& exp) { pretty_print(exp); }, [](lvalue_non_static_field const& exp) { pretty_print(exp); }, [](lvalue_array const& exp) { pretty_print(exp); }, [](lvalue_ambiguous_name const& exp) { pretty_print(exp); } ) , exp);
простым
boost::apply_visitor(pretty_print_visitor, exp);
-
Обратите внимание на несколько случаев, когда я добавил комментарии
// TODO
или// FIXME
(примечание сconcat
, которое не совсем хотело компилировать для меня больше). -
Обратите внимание, что классы Ast значительно упростились (особенно более тривиально правильные относительно распределения памяти)
-
Обратите внимание, что сам Parser сократился из-за уменьшенной потребности в семантических действиях и адаптированных функциях Phoenix.
-
Заметьте, что я сейчас забыл о информации о LexerPosition (которая раньше была "скрыта" в базовых классах, теперь ушла). Существует пример учебника для компилятора, в котором показано, как использовать
qi::on_error(qi::success, ...)
для очень элегантно прикреплять информацию о местоположении источника к выбранным узлам Ast (не навязчиво). -
Вместо различных предикатов в
ast_helpers
я бы предположил, что может быть множество полезных предикатов на основе признаков (например,is_lvalue
илиis_true_const
). Я решил "сохранить" помощников более или менее как есть (что может быть совершенно неправильно, я ничего не тестировал). -
Я повсеместно пытался заменить передачу параметра по значению на передачу
const&
(сравнить, например, ast_pp.hpp) но я знаю, что я оставил некоторые пятна, потому что задача была достаточно большой, как это было.
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ GIANT. Я, вероятно, по-разному разбил парсер. Я не пытался ничего разбирать с этим. Редакции предоставляются как есть и без каких-либо претензий на полезность. Я решил аналогичные проблемы разными способами (после a traits::tranform_attribute<>
специализации, когда было довольно большое семантическое действие с at_c<>
и некоторыми другими подходами):
Цель состояла в том, чтобы показать вам, что я имел в виду, когда я упомянул, может быть,
- значительно снижает динамический полиморфизм,
- избежание семантических действий
- обдумывая конструкции boost, где возможно, чтобы получить более "автоматическую" интеграцию с духом
- показывая различные идеи, вы можете выбрать своих фаворитов из:/