chaiscript_optimizer.hpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. // This file is distributed under the BSD License.
  2. // See "license.txt" for details.
  3. // Copyright 2009-2012, Jonathan Turner ([email protected])
  4. // Copyright 2009-2017, Jason Turner ([email protected])
  5. // http://www.chaiscript.com
  6. #ifndef CHAISCRIPT_OPTIMIZER_HPP_
  7. #define CHAISCRIPT_OPTIMIZER_HPP_
  8. #include "chaiscript_eval.hpp"
  9. namespace chaiscript {
  10. namespace optimizer {
  11. template<typename ... T>
  12. struct Optimizer : T...
  13. {
  14. Optimizer() = default;
  15. explicit Optimizer(T ... t)
  16. : T(std::move(t))...
  17. {
  18. }
  19. template<typename Tracer>
  20. auto optimize(eval::AST_Node_Impl_Ptr<Tracer> p) {
  21. (void)std::initializer_list<int>{ (p = static_cast<T&>(*this).optimize(std::move(p)), 0)... };
  22. return p;
  23. }
  24. };
  25. template<typename T>
  26. eval::AST_Node_Impl<T> &child_at(eval::AST_Node_Impl<T> &node, const size_t offset) {
  27. if (node.children[offset]->identifier == AST_Node_Type::Compiled) {
  28. return *(dynamic_cast<eval::Compiled_AST_Node<T> &>(*node.children[offset]).m_original_node);
  29. } else {
  30. return *node.children[offset];
  31. }
  32. }
  33. template<typename T>
  34. const eval::AST_Node_Impl<T> &child_at(const eval::AST_Node_Impl<T> &node, const size_t offset) {
  35. if (node.children[offset]->identifier == AST_Node_Type::Compiled) {
  36. return *(dynamic_cast<const eval::Compiled_AST_Node<T> &>(*node.children[offset]).m_original_node);
  37. } else {
  38. return *node.children[offset];
  39. }
  40. /*
  41. if (node->identifier == AST_Node_Type::Compiled) {
  42. return dynamic_cast<const eval::Compiled_AST_Node<T>&>(*node).m_original_node->children[offset];
  43. } else {
  44. return node->children[offset];
  45. }
  46. */
  47. }
  48. template<typename T>
  49. auto child_count(const eval::AST_Node_Impl<T> &node) {
  50. if (node.identifier == AST_Node_Type::Compiled) {
  51. return dynamic_cast<const eval::Compiled_AST_Node<T>&>(node).m_original_node->children.size();
  52. } else {
  53. return node.children.size();
  54. }
  55. }
  56. template<typename T, typename Callable>
  57. auto make_compiled_node(eval::AST_Node_Impl_Ptr<T> original_node, std::vector<eval::AST_Node_Impl_Ptr<T>> children, Callable callable)
  58. {
  59. return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Compiled_AST_Node<T>>(std::move(original_node), std::move(children), std::move(callable));
  60. }
  61. struct Return {
  62. template<typename T>
  63. auto optimize(eval::AST_Node_Impl_Ptr<T> p)
  64. {
  65. if ( (p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda)
  66. && !p->children.empty())
  67. {
  68. auto &last_child = p->children.back();
  69. if (last_child->identifier == AST_Node_Type::Block) {
  70. auto &block_last_child = last_child->children.back();
  71. if (block_last_child->identifier == AST_Node_Type::Return) {
  72. if (block_last_child->children.size() == 1) {
  73. last_child->children.back() = std::move(block_last_child->children[0]);
  74. }
  75. }
  76. }
  77. }
  78. return p;
  79. }
  80. };
  81. template<typename T>
  82. bool contains_var_decl_in_scope(const eval::AST_Node_Impl<T> &node)
  83. {
  84. if (node.identifier == AST_Node_Type::Var_Decl) {
  85. return true;
  86. }
  87. const auto num = child_count(node);
  88. for (size_t i = 0; i < num; ++i) {
  89. const auto &child = child_at(node, i);
  90. if (child.identifier != AST_Node_Type::Block
  91. && child.identifier != AST_Node_Type::For
  92. && contains_var_decl_in_scope(child)) {
  93. return true;
  94. }
  95. }
  96. return false;
  97. }
  98. struct Block {
  99. template<typename T>
  100. auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
  101. if (node->identifier == AST_Node_Type::Block)
  102. {
  103. if (!contains_var_decl_in_scope(*node))
  104. {
  105. if (node->children.size() == 1) {
  106. return std::move(node->children[0]);
  107. } else {
  108. return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Scopeless_Block_AST_Node<T>>(node->text, node->location,
  109. std::move(node->children));
  110. }
  111. }
  112. }
  113. return node;
  114. }
  115. };
  116. struct Dead_Code {
  117. template<typename T>
  118. auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
  119. if (node->identifier == AST_Node_Type::Block)
  120. {
  121. std::vector<size_t> keepers;
  122. const auto num_children = node->children.size();
  123. keepers.reserve(num_children);
  124. for (size_t i = 0; i < num_children; ++i) {
  125. const auto &child = *node->children[i];
  126. if ( (child.identifier != AST_Node_Type::Id
  127. && child.identifier != AST_Node_Type::Constant
  128. && child.identifier != AST_Node_Type::Noop)
  129. || i == num_children - 1) {
  130. keepers.push_back(i);
  131. }
  132. }
  133. if (keepers.size() == num_children) {
  134. return node;
  135. } else {
  136. const auto new_children = [&](){
  137. std::vector<eval::AST_Node_Impl_Ptr<T>> retval;
  138. for (const auto x : keepers)
  139. {
  140. retval.push_back(std::move(node->children[x]));
  141. }
  142. return retval;
  143. };
  144. return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Block_AST_Node<T>>(node->text, node->location, new_children());
  145. }
  146. } else {
  147. return node;
  148. }
  149. }
  150. };
  151. struct Unused_Return {
  152. template<typename T>
  153. auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
  154. if ((node->identifier == AST_Node_Type::Block
  155. || node->identifier == AST_Node_Type::Scopeless_Block)
  156. && !node->children.empty())
  157. {
  158. for (size_t i = 0; i < node->children.size()-1; ++i) {
  159. auto child = node->children[i].get();
  160. if (child->identifier == AST_Node_Type::Fun_Call) {
  161. node->children[i] = chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Unused_Return_Fun_Call_AST_Node<T>>(child->text, child->location,
  162. std::move(child->children));
  163. }
  164. }
  165. } else if ((node->identifier == AST_Node_Type::For
  166. || node->identifier == AST_Node_Type::While)
  167. && child_count(*node) > 0) {
  168. auto &child = child_at(*node, child_count(*node) - 1);
  169. if (child.identifier == AST_Node_Type::Block
  170. || child.identifier == AST_Node_Type::Scopeless_Block)
  171. {
  172. auto num_sub_children = child_count(child);
  173. for (size_t i = 0; i < num_sub_children; ++i) {
  174. auto &sub_child = child_at(child, i);
  175. if (sub_child.identifier == AST_Node_Type::Fun_Call) {
  176. 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));
  177. }
  178. }
  179. }
  180. }
  181. return node;
  182. }
  183. };
  184. struct If {
  185. template<typename T>
  186. auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
  187. if ((node->identifier == AST_Node_Type::If)
  188. && node->children.size() >= 2
  189. && node->children[0]->identifier == AST_Node_Type::Constant)
  190. {
  191. const auto condition = dynamic_cast<eval::Constant_AST_Node<T> *>(node->children[0].get())->m_value;
  192. if (condition.get_type_info().bare_equal_type_info(typeid(bool))) {
  193. if (boxed_cast<bool>(condition)) {
  194. return std::move(node->children[1]);
  195. } else if (node->children.size() == 3) {
  196. return std::move(node->children[2]);
  197. }
  198. }
  199. }
  200. return node;
  201. }
  202. };
  203. struct Partial_Fold {
  204. template<typename T>
  205. auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
  206. // Fold right side
  207. if (node->identifier == AST_Node_Type::Binary
  208. && node->children.size() == 2
  209. && node->children[0]->identifier != AST_Node_Type::Constant
  210. && node->children[1]->identifier == AST_Node_Type::Constant)
  211. {
  212. try {
  213. const auto &oper = node->text;
  214. const auto parsed = Operators::to_operator(oper);
  215. if (parsed != Operators::Opers::invalid) {
  216. const auto rhs = dynamic_cast<eval::Constant_AST_Node<T> *>(node->children[1].get())->m_value;
  217. if (rhs.get_type_info().is_arithmetic()) {
  218. return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Fold_Right_Binary_Operator_AST_Node<T>>(node->text, node->location,
  219. std::move(node->children), rhs);
  220. }
  221. }
  222. } catch (const std::exception &) {
  223. //failure to fold, that's OK
  224. }
  225. }
  226. return node;
  227. }
  228. };
  229. struct Constant_Fold {
  230. template<typename T>
  231. auto optimize(eval::AST_Node_Impl_Ptr<T> node) {
  232. if (node->identifier == AST_Node_Type::Prefix
  233. && node->children.size() == 1
  234. && node->children[0]->identifier == AST_Node_Type::Constant)
  235. {
  236. try {
  237. const auto &oper = node->text;
  238. const auto parsed = Operators::to_operator(oper, true);
  239. const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> *>(node->children[0].get())->m_value;
  240. const auto match = oper + node->children[0]->text;
  241. if (parsed != Operators::Opers::invalid && parsed != Operators::Opers::bitwise_and && lhs.get_type_info().is_arithmetic()) {
  242. const auto val = Boxed_Number::do_oper(parsed, lhs);
  243. return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
  244. } else if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && oper == "!") {
  245. 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)));
  246. }
  247. } catch (const std::exception &) {
  248. //failure to fold, that's OK
  249. }
  250. } else if ((node->identifier == AST_Node_Type::Logical_And || node->identifier == AST_Node_Type::Logical_Or)
  251. && node->children.size() == 2
  252. && node->children[0]->identifier == AST_Node_Type::Constant
  253. && node->children[1]->identifier == AST_Node_Type::Constant)
  254. {
  255. try {
  256. const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[0]).m_value;
  257. const auto rhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]).m_value;
  258. if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && rhs.get_type_info().bare_equal_type_info(typeid(bool))) {
  259. const auto match = node->children[0]->text + " " + node->text + " " + node->children[1]->text;
  260. const auto val = [lhs_val = boxed_cast<bool>(lhs), rhs_val = boxed_cast<bool>(rhs), id = node->identifier] {
  261. if (id == AST_Node_Type::Logical_And) { return Boxed_Value(lhs_val && rhs_val); }
  262. else { return Boxed_Value(lhs_val || rhs_val); }
  263. }();
  264. return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
  265. }
  266. } catch (const std::exception &) {
  267. //failure to fold, that's OK
  268. }
  269. } else if (node->identifier == AST_Node_Type::Binary
  270. && node->children.size() == 2
  271. && node->children[0]->identifier == AST_Node_Type::Constant
  272. && node->children[1]->identifier == AST_Node_Type::Constant)
  273. {
  274. try {
  275. const auto &oper = node->text;
  276. const auto parsed = Operators::to_operator(oper);
  277. if (parsed != Operators::Opers::invalid) {
  278. const auto lhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[0]).m_value;
  279. const auto rhs = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]).m_value;
  280. if (lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) {
  281. const auto val = Boxed_Number::do_oper(parsed, lhs, rhs);
  282. const auto match = node->children[0]->text + " " + oper + " " + node->children[1]->text;
  283. return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, std::move(val));
  284. }
  285. }
  286. } catch (const std::exception &) {
  287. //failure to fold, that's OK
  288. }
  289. } else if (node->identifier == AST_Node_Type::Fun_Call
  290. && node->children.size() == 2
  291. && node->children[0]->identifier == AST_Node_Type::Id
  292. && node->children[1]->identifier == AST_Node_Type::Arg_List
  293. && node->children[1]->children.size() == 1
  294. && node->children[1]->children[0]->identifier == AST_Node_Type::Constant) {
  295. const auto arg = dynamic_cast<const eval::Constant_AST_Node<T> &>(*node->children[1]->children[0]).m_value;
  296. if (arg.get_type_info().is_arithmetic()) {
  297. const auto &fun_name = node->children[0]->text;
  298. const auto make_constant = [&node, &fun_name](auto val){
  299. const auto match = fun_name + "(" + node->children[1]->children[0]->text + ")";
  300. return chaiscript::make_unique<eval::AST_Node_Impl<T>, eval::Constant_AST_Node<T>>(std::move(match), node->location, Boxed_Value(val));
  301. };
  302. if (fun_name == "double") {
  303. return make_constant(Boxed_Number(arg).get_as<double>());
  304. } else if (fun_name == "int") {
  305. return make_constant(Boxed_Number(arg).get_as<int>());
  306. } else if (fun_name == "float") {
  307. return make_constant(Boxed_Number(arg).get_as<float>());
  308. } else if (fun_name == "long") {
  309. return make_constant(Boxed_Number(arg).get_as<long>());
  310. } else if (fun_name == "size_t") {
  311. return make_constant(Boxed_Number(arg).get_as<size_t>());
  312. }
  313. }
  314. }
  315. return node;
  316. }
  317. };
  318. struct For_Loop {
  319. template<typename T>
  320. auto optimize(eval::AST_Node_Impl_Ptr<T> for_node) {
  321. if (for_node->identifier != AST_Node_Type::For) {
  322. return for_node;
  323. }
  324. const auto &eq_node = child_at(*for_node, 0);
  325. const auto &binary_node = child_at(*for_node, 1);
  326. const auto &prefix_node = child_at(*for_node, 2);
  327. if (child_count(*for_node) == 4
  328. && eq_node.identifier == AST_Node_Type::Equation
  329. && child_count(eq_node) == 2
  330. && child_at(eq_node, 0).identifier == AST_Node_Type::Var_Decl
  331. && child_at(eq_node, 1).identifier == AST_Node_Type::Constant
  332. && binary_node.identifier == AST_Node_Type::Binary
  333. && binary_node.text == "<"
  334. && child_count(binary_node) == 2
  335. && child_at(binary_node, 0).identifier == AST_Node_Type::Id
  336. && child_at(binary_node, 0).text == child_at(child_at(eq_node,0), 0).text
  337. && child_at(binary_node, 1).identifier == AST_Node_Type::Constant
  338. && prefix_node.identifier == AST_Node_Type::Prefix
  339. && prefix_node.text == "++"
  340. && child_count(prefix_node) == 1
  341. && child_at(prefix_node, 0).identifier == AST_Node_Type::Id
  342. && child_at(prefix_node, 0).text == child_at(child_at(eq_node,0), 0).text)
  343. {
  344. const Boxed_Value &begin = dynamic_cast<const eval::Constant_AST_Node<T> &>(child_at(eq_node, 1)).m_value;
  345. const Boxed_Value &end = dynamic_cast<const eval::Constant_AST_Node<T> &>(child_at(binary_node, 1)).m_value;
  346. const std::string &id = child_at(prefix_node, 0).text;
  347. if (begin.get_type_info().bare_equal(user_type<int>())
  348. && end.get_type_info().bare_equal(user_type<int>())) {
  349. const auto start_int = boxed_cast<int>(begin);
  350. const auto end_int = boxed_cast<int>(end);
  351. // note that we are moving the last element out, then popping the empty shared_ptr
  352. // from the vector
  353. std::vector<eval::AST_Node_Impl_Ptr<T>> body_vector;
  354. auto body_child = std::move(for_node->children[3]);
  355. for_node->children.pop_back();
  356. body_vector.emplace_back(std::move(body_child));
  357. return make_compiled_node(std::move(for_node), std::move(body_vector),
  358. [id, start_int, end_int](const std::vector<eval::AST_Node_Impl_Ptr<T>> &children, const chaiscript::detail::Dispatch_State &t_ss) {
  359. assert(children.size() == 1);
  360. chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
  361. int i = start_int;
  362. t_ss.add_object(id, var(&i));
  363. try {
  364. for (; i < end_int; ++i) {
  365. try {
  366. // Body of Loop
  367. children[0]->eval(t_ss);
  368. } catch (eval::detail::Continue_Loop &) {
  369. // we got a continue exception, which means all of the remaining
  370. // loop implementation is skipped and we just need to continue to
  371. // the next iteration step
  372. }
  373. }
  374. } catch (eval::detail::Break_Loop &) {
  375. // loop broken
  376. }
  377. return void_var();
  378. }
  379. );
  380. } else {
  381. return for_node;
  382. }
  383. } else {
  384. return for_node;
  385. }
  386. }
  387. };
  388. typedef Optimizer<optimizer::Partial_Fold, optimizer::Unused_Return, optimizer::Constant_Fold,
  389. optimizer::If, optimizer::Return, optimizer::Dead_Code, optimizer::Block, optimizer::For_Loop> Optimizer_Default;
  390. }
  391. }
  392. #endif