| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- // This file is distributed under the BSD License.
- // See "license.txt" for details.
- // Copyright 2009-2012, Jonathan Turner ([email protected])
- // Copyright 2009-2017, Jason Turner ([email protected])
- // http://www.chaiscript.com
- #ifndef CHAISCRIPT_OPTIMIZER_HPP_
- #define CHAISCRIPT_OPTIMIZER_HPP_
- #include "chaiscript_eval.hpp"
- namespace chaiscript {
- namespace optimizer {
- template<typename ... T>
- struct Optimizer : T...
- {
- Optimizer() = default;
- explicit Optimizer(T ... t)
- : T(std::move(t))...
- {
- }
- template<typename Tracer>
- auto optimize(eval::AST_Node_Impl_Ptr<Tracer> p) {
- (void)std::initializer_list<int>{ (p = static_cast<T&>(*this).optimize(std::move(p)), 0)... };
- return p;
- }
- };
- template<typename T>
- eval::AST_Node_Impl<T> &child_at(eval::AST_Node_Impl<T> &node, const size_t offset) {
- if (node.children[offset]->identifier == AST_Node_Type::Compiled) {
- return *(dynamic_cast<eval::Compiled_AST_Node<T> &>(*node.children[offset]).m_original_node);
- } else {
- return *node.children[offset];
- }
- }
- template<typename T>
- const eval::AST_Node_Impl<T> &child_at(const eval::AST_Node_Impl<T> &node, const size_t offset) {
- if (node.children[offset]->identifier == AST_Node_Type::Compiled) {
- return *(dynamic_cast<const eval::Compiled_AST_Node<T> &>(*node.children[offset]).m_original_node);
- } else {
- return *node.children[offset];
- }
- /*
- if (node->identifier == AST_Node_Type::Compiled) {
- return dynamic_cast<const eval::Compiled_AST_Node<T>&>(*node).m_original_node->children[offset];
- } else {
- return node->children[offset];
- }
- */
- }
- template<typename T>
- auto child_count(const eval::AST_Node_Impl<T> &node) {
- if (node.identifier == AST_Node_Type::Compiled) {
- return dynamic_cast<const eval::Compiled_AST_Node<T>&>(node).m_original_node->children.size();
- } else {
- return node.children.size();
- }
- }
- template<typename T, typename Callable>
- auto make_compiled_node(eval::AST_Node_Impl_Ptr<T> original_node, std::vector<eval::AST_Node_Impl_Ptr<T>> children, Callable callable)
- {
- return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Compiled_AST_Node<T>>(std::move(original_node), std::move(children), std::move(callable));
- }
- struct Return {
- template<typename T>
- auto optimize(eval::AST_Node_Impl_Ptr<T> p)
- {
- if ( (p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda)
- && !p->children.empty())
- {
- auto &last_child = p->children.back();
- if (last_child->identifier == AST_Node_Type::Block) {
- auto &block_last_child = last_child->children.back();
- if (block_last_child->identifier == AST_Node_Type::Return) {
- if (block_last_child->children.size() == 1) {
- last_child->children.back() = std::move(block_last_child->children[0]);
- }
- }
- }
- }
- return p;
- }
- };
- template<typename T>
- bool contains_var_decl_in_scope(const eval::AST_Node_Impl<T> &node)
- {
- if (node.identifier == AST_Node_Type::Var_Decl) {
- return true;
- }
- const auto num = child_count(node);
- for (size_t i = 0; i < num; ++i) {
- const auto &child = child_at(node, i);
- if (child.identifier != AST_Node_Type::Block
- && child.identifier != AST_Node_Type::For
- && contains_var_decl_in_scope(child)) {
- return true;
- }
- }
- return false;
- }
- struct Block {
- template<typename T>
- auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
- if (node->identifier == AST_Node_Type::Block)
- {
- if (!contains_var_decl_in_scope(*node))
- {
- if (node->children.size() == 1) {
- return std::move(node->children[0]);
- } else {
- return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Scopeless_Block_AST_Node<T>>(node->text, node->location,
- std::move(node->children));
- }
- }
- }
- return node;
- }
- };
- struct Dead_Code {
- template<typename T>
- auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
- if (node->identifier == AST_Node_Type::Block)
- {
- std::vector<size_t> keepers;
- const auto num_children = node->children.size();
- keepers.reserve(num_children);
- for (size_t i = 0; i < num_children; ++i) {
- const auto &child = *node->children[i];
- if ( (child.identifier != AST_Node_Type::Id
- && child.identifier != AST_Node_Type::Constant
- && child.identifier != AST_Node_Type::Noop)
- || i == num_children - 1) {
- keepers.push_back(i);
- }
- }
- if (keepers.size() == num_children) {
- return node;
- } else {
- const auto new_children = [&](){
- std::vector<eval::AST_Node_Impl_Ptr<T>> retval;
- for (const auto x : keepers)
- {
- retval.push_back(std::move(node->children[x]));
- }
- return retval;
- };
- return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Block_AST_Node<T>>(node->text, node->location, new_children());
- }
- } else {
- return node;
- }
- }
- };
- struct Unused_Return {
- template<typename T>
- auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
- if ((node->identifier == AST_Node_Type::Block
- || node->identifier == AST_Node_Type::Scopeless_Block)
- && !node->children.empty())
- {
- for (size_t i = 0; i < node->children.size()-1; ++i) {
- auto child = node->children[i].get();
- if (child->identifier == AST_Node_Type::Fun_Call) {
- node->children[i] = chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(child->text, child->location,
- std::move(child->children));
- }
- }
- } else if ((node->identifier == AST_Node_Type::For
- || node->identifier == AST_Node_Type::While)
- && child_count(*node) > 0) {
- auto &child = child_at(*node, child_count(*node) - 1);
- if (child.identifier == AST_Node_Type::Block
- || child.identifier == AST_Node_Type::Scopeless_Block)
- {
- auto num_sub_children = child_count(child);
- for (size_t i = 0; i < num_sub_children; ++i) {
- auto &sub_child = child_at(child, i);
- if (sub_child.identifier == AST_Node_Type::Fun_Call) {
- child.children[i] = chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(sub_child.text, sub_child.location, std::move(sub_child.children));
- }
- }
- }
- }
- return node;
- }
- };
- struct If {
- template<typename T>
- auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
- if ((node->identifier == AST_Node_Type::If)
- && node->children.size() >= 2
- && node->children[0]->identifier == AST_Node_Type::Constant)
- {
- const auto condition = dynamic_cast<eval::Constant_AST_Node<T> *>(node->children[0].get())->m_value;
- if (condition.get_type_info().bare_equal_type_info(typeid(bool))) {
- if (boxed_cast<bool>(condition)) {
- return std::move(node->children[1]);
- } else if (node->children.size() == 3) {
- return std::move(node->children[2]);
- }
- }
- }
- return node;
- }
- };
- struct Partial_Fold {
- template<typename T>
- auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
- // Fold right side
- if (node->identifier == AST_Node_Type::Binary
- && node->children.size() == 2
- && node->children[0]->identifier != AST_Node_Type::Constant
- && node->children[1]->identifier == AST_Node_Type::Constant)
- {
- try {
- const auto &oper = node->text;
- const auto parsed = Operators::to_operator(oper);
- if (parsed != Operators::Opers::invalid) {
- const auto rhs = dynamic_cast<eval::Constant_AST_Node<T> *>(node->children[1].get())->m_value;
- if (rhs.get_type_info().is_arithmetic()) {
- return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Fold_Right_Binary_Operator_AST_Node<T>>(node->text, node->location,
- std::move(node->children), rhs);
- }
- }
- } catch (const std::exception &) {
- //failure to fold, that's OK
- }
- }
- return node;
- }
- };
- struct Constant_Fold {
- template<typename T>
- auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
- if (node->identifier == AST_Node_Type::Prefix
- && node->children.size() == 1
- && node->children[0]->identifier == AST_Node_Type::Constant)
- {
- try {
- const auto &oper = node->text;
- const auto parsed = Operators::to_operator(oper, true);
- const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> *>(node->children[0].get())->m_value;
- const auto match = oper + node->children[0]->text;
- if (parsed != Operators::Opers::invalid && parsed != Operators::Opers::bitwise_and && lhs.get_type_info().is_arithmetic()) {
- const auto val = Boxed_Number::do_oper(parsed, lhs);
- return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
- } else if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && oper == "!") {
- return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(!boxed_cast<bool>(lhs)));
- }
- } catch (const std::exception &) {
- //failure to fold, that's OK
- }
- } else if ((node->identifier == AST_Node_Type::Logical_And || node->identifier == AST_Node_Type::Logical_Or)
- && node->children.size() == 2
- && node->children[0]->identifier == AST_Node_Type::Constant
- && node->children[1]->identifier == AST_Node_Type::Constant)
- {
- try {
- const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[0]).m_value;
- const auto rhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]).m_value;
- if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && rhs.get_type_info().bare_equal_type_info(typeid(bool))) {
- const auto match = node->children[0]->text + " " + node->text + " " + node->children[1]->text;
- const auto val = [lhs_val = boxed_cast<bool>(lhs), rhs_val = boxed_cast<bool>(rhs), id = node->identifier] {
- if (id == AST_Node_Type::Logical_And) { return Boxed_Value(lhs_val && rhs_val); }
- else { return Boxed_Value(lhs_val || rhs_val); }
- }();
- return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
- }
- } catch (const std::exception &) {
- //failure to fold, that's OK
- }
- } else if (node->identifier == AST_Node_Type::Binary
- && node->children.size() == 2
- && node->children[0]->identifier == AST_Node_Type::Constant
- && node->children[1]->identifier == AST_Node_Type::Constant)
- {
- try {
- const auto &oper = node->text;
- const auto parsed = Operators::to_operator(oper);
- if (parsed != Operators::Opers::invalid) {
- const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[0]).m_value;
- const auto rhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]).m_value;
- if (lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) {
- const auto val = Boxed_Number::do_oper(parsed, lhs, rhs);
- const auto match = node->children[0]->text + " " + oper + " " + node->children[1]->text;
- return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
- }
- }
- } catch (const std::exception &) {
- //failure to fold, that's OK
- }
- } else if (node->identifier == AST_Node_Type::Fun_Call
- && node->children.size() == 2
- && node->children[0]->identifier == AST_Node_Type::Id
- && node->children[1]->identifier == AST_Node_Type::Arg_List
- && node->children[1]->children.size() == 1
- && node->children[1]->children[0]->identifier == AST_Node_Type::Constant) {
- const auto arg = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]->children[0]).m_value;
- if (arg.get_type_info().is_arithmetic()) {
- const auto &fun_name = node->children[0]->text;
- const auto make_constant = [&node, &fun_name](auto val){
- const auto match = fun_name + "(" + node->children[1]->children[0]->text + ")";
- return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(val));
- };
- if (fun_name == "double") {
- return make_constant(Boxed_Number(arg).get_as<double>());
- } else if (fun_name == "int") {
- return make_constant(Boxed_Number(arg).get_as<int>());
- } else if (fun_name == "float") {
- return make_constant(Boxed_Number(arg).get_as<float>());
- } else if (fun_name == "long") {
- return make_constant(Boxed_Number(arg).get_as<long>());
- } else if (fun_name == "size_t") {
- return make_constant(Boxed_Number(arg).get_as<size_t>());
- }
- }
- }
- return node;
- }
- };
- struct For_Loop {
- template<typename T>
- auto optimize(eval::AST_Node_Impl_Ptr<T> for_node) {
- if (for_node->identifier != AST_Node_Type::For) {
- return for_node;
- }
- const auto &eq_node = child_at(*for_node, 0);
- const auto &binary_node = child_at(*for_node, 1);
- const auto &prefix_node = child_at(*for_node, 2);
- if (child_count(*for_node) == 4
- && eq_node.identifier == AST_Node_Type::Equation
- && child_count(eq_node) == 2
- && child_at(eq_node, 0).identifier == AST_Node_Type::Var_Decl
- && child_at(eq_node, 1).identifier == AST_Node_Type::Constant
- && binary_node.identifier == AST_Node_Type::Binary
- && binary_node.text == "<"
- && child_count(binary_node) == 2
- && child_at(binary_node, 0).identifier == AST_Node_Type::Id
- && child_at(binary_node, 0).text == child_at(child_at(eq_node,0), 0).text
- && child_at(binary_node, 1).identifier == AST_Node_Type::Constant
- && prefix_node.identifier == AST_Node_Type::Prefix
- && prefix_node.text == "++"
- && child_count(prefix_node) == 1
- && child_at(prefix_node, 0).identifier == AST_Node_Type::Id
- && child_at(prefix_node, 0).text == child_at(child_at(eq_node,0), 0).text)
- {
- const Boxed_Value &begin = dynamic_cast<const eval::Constant_AST_Node<T> &>(child_at(eq_node, 1)).m_value;
- const Boxed_Value &end = dynamic_cast<const eval::Constant_AST_Node<T> &>(child_at(binary_node, 1)).m_value;
- const std::string &id = child_at(prefix_node, 0).text;
- if (begin.get_type_info().bare_equal(user_type<int>())
- && end.get_type_info().bare_equal(user_type<int>())) {
- const auto start_int = boxed_cast<int>(begin);
- const auto end_int = boxed_cast<int>(end);
- // note that we are moving the last element out, then popping the empty shared_ptr
- // from the vector
- std::vector<eval::AST_Node_Impl_Ptr<T>> body_vector;
- auto body_child = std::move(for_node->children[3]);
- for_node->children.pop_back();
- body_vector.emplace_back(std::move(body_child));
-
- return make_compiled_node(std::move(for_node), std::move(body_vector),
- [id, start_int, end_int](const std::vector<eval::AST_Node_Impl_Ptr<T>> &children, const chaiscript::detail::Dispatch_State &t_ss) {
- assert(children.size() == 1);
- chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
- int i = start_int;
- t_ss.add_object(id, var(&i));
- try {
- for (; i < end_int; ++i) {
- try {
- // Body of Loop
- children[0]->eval(t_ss);
- } catch (eval::detail::Continue_Loop &) {
- // we got a continue exception, which means all of the remaining
- // loop implementation is skipped and we just need to continue to
- // the next iteration step
- }
- }
- } catch (eval::detail::Break_Loop &) {
- // loop broken
- }
- return void_var();
- }
- );
- } else {
- return for_node;
- }
- } else {
- return for_node;
- }
- }
- };
- typedef Optimizer<optimizer::Partial_Fold, optimizer::Unused_Return, optimizer::Constant_Fold,
- optimizer::If, optimizer::Return, optimizer::Dead_Code, optimizer::Block, optimizer::For_Loop> Optimizer_Default;
- }
- }
- #endif
|