From 6ba3e92d6e317fce0a2fc51ea4956109b7f9a9e3 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 24 Mar 2015 14:15:47 -0600 Subject: [PATCH] Various tree optimizations --- .../chaiscript/language/chaiscript_common.hpp | 4 +- .../chaiscript/language/chaiscript_eval.hpp | 102 +++++++++++++++++- .../chaiscript/language/chaiscript_parser.hpp | 83 ++++++++++++-- 3 files changed, 174 insertions(+), 15 deletions(-) diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 6a01d67..9195fb2 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -467,14 +467,14 @@ namespace chaiscript } protected: - AST_Node(std::string t_ast_node_text, int t_id, const std::shared_ptr &t_fname, + AST_Node(std::string t_ast_node_text, int t_id, const std::shared_ptr &t_fname, int t_start_line, int t_start_col, int t_end_line, int t_end_col) : text(std::move(t_ast_node_text)), identifier(t_id), filename(t_fname), start(t_start_line, t_start_col), end(t_end_line, t_end_col) { } - AST_Node(std::string t_ast_node_text, int t_id, const std::shared_ptr &t_fname) : + AST_Node(std::string t_ast_node_text, int t_id, const std::shared_ptr &t_fname) : text(std::move(t_ast_node_text)), identifier(t_id), filename(t_fname) {} virtual ~AST_Node() {} diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 8c519e8..ff3a72e 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -218,6 +218,7 @@ namespace chaiscript } }; + struct Fun_Call_AST_Node : public AST_Node { public: Fun_Call_AST_Node(const std::string &t_ast_node_text = "", const std::shared_ptr &t_fname=std::shared_ptr(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) : @@ -289,6 +290,96 @@ namespace chaiscript }; + struct Fun_Lookup_AST_Node : public AST_Node { + public: + Fun_Lookup_AST_Node(const std::string &t_fun_name) + : AST_Node(t_fun_name, 0, std::make_shared("")) + { + } + + virtual ~Fun_Lookup_AST_Node() {} + + virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE { + try { + Boxed_Value bv = t_ss.get_object(text); + t_ss.add_object(text, bv); + std::cout << " Saved fun lookup: " << text << '\n'; + return bv; + } catch (...) { + return Boxed_Value(); + } + } + }; + + + + struct Unary_Fun_Call_AST_Node : public AST_Node { + public: + Unary_Fun_Call_AST_Node(const Fun_Call_AST_Node &t_fc) + : AST_Node(t_fc.text, t_fc.identifier, t_fc.filename, t_fc.start.line, t_fc.start.column, t_fc.end.line, t_fc.end.column) + { + this->children = t_fc.children; + } + virtual ~Unary_Fun_Call_AST_Node() {} + + virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE{ + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + + std::vector params{children[1]->children[0]->eval(t_ss)}; + fpp.save_params(params); + + Boxed_Value fn(this->children[0]->eval(t_ss)); + + try { + chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); + return (*t_ss.boxed_cast(fn))(params, t_ss.conversions()); + } + catch(const exception::dispatch_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'", e.parameters, e.functions, false, t_ss); + } + catch(const exception::bad_boxed_cast &){ + try { + Const_Proxy_Function f = t_ss.boxed_cast(fn); + // handle the case where there is only 1 function to try to call and dispatch fails on it + throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, {f}, false, t_ss); + } catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function."); + } + } + catch(const exception::arity_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } + catch(const exception::guard_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } + catch(detail::Return_Value &rv) { + return rv.retval; + } + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + std::ostringstream oss; + + int count = 0; + for (const auto &child : this->children) { + oss << child->pretty_print(); + + if (count == 0) + { + oss << "("; + } + ++count; + } + + oss << ")"; + + return oss.str(); + } + + }; + + /// Used in the context of in-string ${} evals, so that no new scope is created struct Inplace_Fun_Call_AST_Node : public AST_Node { public: @@ -452,23 +543,24 @@ namespace chaiscript AST_Node(std::move(t_ast_node_text), AST_Node_Type::Equation, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) {} + Operators::Opers m_oper; + virtual ~Equation_AST_Node() {} virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - Boxed_Value rhs = this->children.back()->eval(t_ss); + Boxed_Value rhs = this->children[2]->eval(t_ss); Boxed_Value lhs = this->children[0]->eval(t_ss); - Operators::Opers oper = Operators::to_operator(this->children[1]->text); - if (oper != Operators::invalid && lhs.get_type_info().is_arithmetic() && + if (m_oper != Operators::invalid && lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) { try { - return Boxed_Number::do_oper(oper, lhs, rhs); + return Boxed_Number::do_oper(m_oper, lhs, rhs); } catch (const std::exception &) { throw exception::eval_error("Error with unsupported arithmetic assignment operation"); } - } else if (oper == Operators::assign) { + } else if (m_oper == Operators::assign) { try { if (lhs.is_undef()) { if (!this->children.empty() && diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index ca0ad33..cac574a 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -196,13 +196,55 @@ namespace chaiscript return m_match_stack.front(); } + static std::map count_fun_calls(const AST_NodePtr &p, bool in_loop) { + if (p->identifier == AST_Node_Type::Fun_Call) { + if (p->children[0]->identifier == AST_Node_Type::Id) { + return std::map{{p->children[0]->text, in_loop?99:1}}; + } + return {}; + } else { + std::map counts; + for (const auto &child : p->children) { + auto childcounts = count_fun_calls(child, in_loop || p->identifier == AST_Node_Type::For || p->identifier == AST_Node_Type::While); + for (const auto &count : childcounts) { + counts[count.first] += count.second; + } + } + return counts; + } + + } + + static void optimize_fun_lookups(AST_NodePtr &p) + { + for (auto &c : p->children) + { + + if (c->identifier == AST_Node_Type::Def + || c->identifier == AST_Node_Type::Method + || c->identifier == AST_Node_Type::Lambda) { + std::vector children_to_add; + auto counts = count_fun_calls(c, false); + for (const auto &count : counts) { + // std::cout << " Fun Call Count: " << count.first << " " << count.second << '\n'; + if (count.second > 1) { + children_to_add.push_back(std::make_shared(count.first)); + } + } + c->children.back()->children.insert(c->children.back()->children.begin(), children_to_add.begin(), children_to_add.end()); + } + optimize_fun_lookups(c); + } + } + + static void optimize_blocks(AST_NodePtr &p) { for (auto &c : p->children) { if (c->identifier == AST_Node_Type::Block) { if (c->children.size() == 1) { - std::cout << "swapping out block child for block\n"; + // std::cout << "swapping out block child for block\n"; c = c->children[0]; } } @@ -229,6 +271,30 @@ namespace chaiscript } } + static void optimize_fun_calls(AST_NodePtr &p) + { + for (auto &c : p->children) + { + if (c->identifier == AST_Node_Type::Fun_Call && c->children.size() == 2 && c->children[1]->children.size() == 1) { + c = std::make_shared(dynamic_cast(*c)); + // std::cout << "optimized unary fun call\n"; + } + optimize_fun_calls(c); + } + } + + static void fixup_opers(AST_NodePtr &p) + { + if (p->identifier == AST_Node_Type::Equation) + { + dynamic_cast(*p).m_oper = Operators::to_operator(p->children[1]->text); + } + + for (auto &c : p->children) { + fixup_opers(c); + } + } + static int count_nodes(const AST_NodePtr &p) { int count = 1; @@ -238,14 +304,15 @@ namespace chaiscript return count; } - AST_NodePtr optimized_ast() { - std::cout << " Optimizing AST \n"; + AST_NodePtr optimized_ast(bool t_optimize_blocks = false, bool t_optimize_returns = true, bool t_optimize_fun_lookups = false, + bool t_optimize_fun_calls = false) { AST_NodePtr p = m_match_stack.front(); - std::cout << "Node Count: " << count_nodes(p) << '\n'; -// optimize_blocks(p); -// std::cout << "Optimized Block Node Count: " << count_nodes(p) << '\n'; - optimize_returns(p); - std::cout << "Returns Block Node Count: " << count_nodes(p) << '\n'; + fixup_opers(p); + //Note, optimize_blocks is currently broken; it breaks stack management + if (t_optimize_blocks) { optimize_blocks(p); } + if (t_optimize_returns) { optimize_returns(p); } + if (t_optimize_fun_lookups) { optimize_fun_lookups(p); } + if (t_optimize_fun_calls) { optimize_fun_calls(p); } return p; }