You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

367 regels
12 KiB

11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
  1. #include <iostream>
  2. #include <iomanip>
  3. #include <algorithm>
  4. #include <chrono>
  5. #include <fstream>
  6. #include <span>
  7. #include <cstring>
  8. #include "UserScript.h"
  9. void print_value(std::ostream& stream, const scripting::script_value& res, bool array_print = false) {
  10. if(std::holds_alternative<scripting::array>(res)) {
  11. stream << "[";
  12. auto max = std::get<scripting::array>(res).value.size();
  13. auto no_comma = max - 1;
  14. for(size_t idx = 0; idx < max; ++idx) {
  15. print_value(stream, std::get<scripting::array>(res).value[idx], true);
  16. stream << (idx != no_comma ? ", " : "");
  17. }
  18. stream << "]";
  19. } else if(std::holds_alternative<std::string>(res)) {
  20. if(array_print) {
  21. stream << std::quoted(std::get<std::string>(res));
  22. } else {
  23. stream << std::get<std::string>(res);
  24. }
  25. } else if(std::holds_alternative<scripting::null>(res)) {
  26. stream << "null";
  27. } else {
  28. stream << std::get<int32_t>(res);
  29. }
  30. }
  31. struct print : public scripting::function_impl {
  32. std::ostream& stream;
  33. print(std::ostream& _stream) : stream(_stream) {}
  34. std::optional<scripting::script_value> apply(scripting::UserScript* self,std::vector<scripting::argument> args, std::optional<scripting::script_error>& errors) final {
  35. while(not args.empty()) {
  36. auto& arg = args.back();
  37. if(std::holds_alternative<scripting::script_value>(arg)) {
  38. print_value(stream, std::get<scripting::script_value>(arg));
  39. } else {
  40. print_value(stream, self->resolve(std::get<scripting::script_variable>(arg).name));
  41. }
  42. args.pop_back();
  43. }
  44. return scripting::script_value({});
  45. }
  46. };
  47. struct terminate : public scripting::function_impl {
  48. std::optional<scripting::script_value> apply(scripting::UserScript*,std::vector<scripting::argument>, std::optional<scripting::script_error>&) final {
  49. std::exit(1);
  50. // PLEASE DO NOT ACTUALLY EXIT YOU FUCKING IDIOT
  51. return scripting::script_value({});
  52. }
  53. };
  54. struct fn_exit : public scripting::function_impl {
  55. bool& exit_val;
  56. fn_exit(bool& _exit_val) : exit_val(_exit_val) {}
  57. std::optional<scripting::script_value> apply(scripting::UserScript*,std::vector<scripting::argument>, std::optional<scripting::script_error>&) final {
  58. exit_val = true;
  59. return scripting::script_value({});
  60. }
  61. };
  62. void process_bench(std::string target = "./tests/scripts/testfile.test") {
  63. auto engine = scripting::prepare_interpreter(std::string{});
  64. constexpr scripting::UserScriptLibraryParameters params{};
  65. engine = scripting::register_array_lib(std::move(engine), params);
  66. engine = scripting::register_crypto_lib(std::move(engine), params);
  67. engine = scripting::register_utils_lib(std::move(engine), params);
  68. engine->registerFunction("exit", std::make_unique<terminate>());
  69. /***
  70. * This is a half assed benchmark,
  71. * Document results here to keep the thingy in check performance wise (release mode only)
  72. *
  73. * 2023-07-04 Archivist -> 2618ns - 308ns - 49ns (clang+libstdc++)
  74. * 2023-07-07 Archivist -> 2481ns - 291ns - 46ns (clang+libc++)
  75. * 2023-07-07 Archivist -> 106ns - 12ns - 2ns (clang+march=native+libc++)
  76. * ?????????? Archivist: These results above are weird, the benchmark was updated in case something strange went on
  77. * 2023-07-18 Archivist -> 3446ns - 405ns - 65ns (gcc+libstdc++)
  78. * ?????????? Archivist: Corrected some extra weirdness
  79. * 2023-07-18 Archivist -> 9952µs - 234ns - 30ns (gcc+libstdc++)
  80. *
  81. */
  82. engine->registerFunction("print", std::make_unique<print>(std::cout));
  83. std::ifstream src_str(target);
  84. std::stringstream code;
  85. code << src_str.rdbuf();
  86. int steps = 0;
  87. decltype(std::chrono::high_resolution_clock::now()-std::chrono::high_resolution_clock::now()) per_exec{}, per_step{}, per_op{};
  88. constexpr size_t runs_count = 500;
  89. for(int runs = 0; runs < runs_count; runs++) {
  90. auto res = engine->prepare(code.str());
  91. engine->clear_variables();
  92. auto begin = std::chrono::high_resolution_clock::now();
  93. while (not engine->getValue("exit_ctr").has_value()) {
  94. engine->stepOnce();
  95. steps++;
  96. }
  97. auto end = std::chrono::high_resolution_clock::now();
  98. per_exec += (end - begin);
  99. per_step += (end - begin);
  100. per_op += (end - begin);
  101. }
  102. auto executed_ops = (runs_count * engine->op_count() * 5000 /* The code loops 5000 times */);
  103. per_exec /= runs_count;
  104. per_step /= steps;
  105. per_op = per_op / executed_ops;
  106. std::cout << "time per exec (" << runs_count << ") = " << std::chrono::duration_cast<std::chrono::microseconds>(per_exec).count() << "µs\n";
  107. std::cout << "time per step (" << steps << ", around " << (double)executed_ops / steps << "op/s) = " << std::chrono::duration_cast<std::chrono::nanoseconds>(per_step).count() << "ns\n";
  108. std::cout << "time per avg op (" << executed_ops << " by groups of " << engine->op_count() << ") = " << std::chrono::duration_cast<std::chrono::nanoseconds>(per_op).count() << "ns\n";
  109. }
  110. void compile_bench(std::string target = "./tests/scripts/testfile.test") {
  111. auto engine = scripting::prepare_interpreter(std::string{});
  112. constexpr scripting::UserScriptLibraryParameters params{};
  113. engine = scripting::register_array_lib(std::move(engine), params);
  114. engine = scripting::register_crypto_lib(std::move(engine), params);
  115. engine = scripting::register_utils_lib(std::move(engine), params);
  116. engine->registerFunction("exit", std::make_unique<terminate>());
  117. /***
  118. * Same as above but for compilation times
  119. *
  120. * 2023-07-04 Archivist -> 386µs
  121. * 2023-07-18 Archivist -> 166µs
  122. * 2023-07-18 Archivist -> 156µs
  123. */
  124. engine->registerFunction("print", std::make_unique<print>(std::cout));
  125. std::ifstream src_str("./tests/scripts/testfile.test");
  126. std::stringstream code;
  127. code << src_str.rdbuf();
  128. constexpr size_t daruns_count = 100;
  129. auto begin = std::chrono::high_resolution_clock::now();
  130. for(size_t i = 0; i < daruns_count; ++i) {
  131. [&]() __attribute__((optimize("O0"))) {
  132. auto res = engine->prepare(code.str());
  133. res = engine->prepare(code.str());
  134. res = engine->prepare(code.str());
  135. res = engine->prepare(code.str());
  136. res = engine->prepare(code.str());
  137. res = engine->prepare(code.str());
  138. res = engine->prepare(code.str());
  139. res = engine->prepare(code.str());
  140. res = engine->prepare(code.str());
  141. res = engine->prepare(code.str());
  142. }();
  143. }
  144. auto end = std::chrono::high_resolution_clock::now();
  145. auto per_exec = (end - begin) / 10 / daruns_count;
  146. std::cout << "time per exec = " << std::chrono::duration_cast<std::chrono::microseconds>(per_exec).count() << "µs\n";
  147. }
  148. void compare(std::string target, std::string expect) {
  149. auto engine = scripting::prepare_interpreter(std::string{});
  150. engine->registerFunction("exit", std::make_unique<terminate>());
  151. constexpr scripting::UserScriptLibraryParameters params{};
  152. engine = scripting::register_array_lib(std::move(engine), params);
  153. engine = scripting::register_crypto_lib(std::move(engine), params);
  154. engine = scripting::register_utils_lib(std::move(engine), params);
  155. std::stringstream str;
  156. std::string_view filename_source = target;
  157. std::string_view filename_output = expect;
  158. engine->registerFunction("print", std::make_unique<print>(str));
  159. std::ifstream src_str(std::string{filename_source});
  160. std::stringstream code;
  161. code << src_str.rdbuf();
  162. std::ifstream out_str(std::string{filename_output});
  163. std::stringstream output;
  164. output << out_str.rdbuf();
  165. auto res = engine->executeAtOnce(code.str());
  166. if (std::holds_alternative<scripting::script_value>(res)) {
  167. } else {
  168. auto &errors = std::get<std::vector<scripting::script_error>>(res);
  169. for (auto &line: errors) {
  170. str << line.message << "\n at line " << line.location->line_number << ":"
  171. << line.location->column_number << "\n";
  172. str << " " << *line.location->line_contents << "\n";
  173. str << " " << std::string(line.location->column_number - 1, ' ') << "^\n";
  174. }
  175. }
  176. int status = 0;
  177. while(not output.eof()) {
  178. std::string expected, found;
  179. std::getline(output, expected);
  180. std::getline(str, found);
  181. bool ok = (expected != found);
  182. status+= ok ;
  183. (ok ? std::cerr : std::cout)
  184. << (not ok ? "\033[21;32m" : "\033[1;31m") << expected
  185. << std::string(std::max<size_t>(0, 40 - expected.size()), ' ')<< "| " << found << std::endl;
  186. }
  187. if(status) std::exit(status);
  188. }
  189. void immediate_interactive() {
  190. bool should_exit = false;
  191. auto engine = scripting::prepare_interpreter(std::string{});
  192. constexpr scripting::UserScriptLibraryParameters params{};
  193. engine = scripting::register_array_lib(std::move(engine), params);
  194. engine = scripting::register_crypto_lib(std::move(engine), params);
  195. engine = scripting::register_utils_lib(std::move(engine), params);
  196. engine->registerFunction("exit", std::make_unique<fn_exit>(should_exit));
  197. engine->registerFunction("print", std::make_unique<print>(std::cout));
  198. while (not should_exit) {
  199. std::string code;
  200. std::getline(std::cin, code);
  201. auto res = engine->executeAtOnce(code);
  202. if (std::holds_alternative<scripting::script_value>(res)) {
  203. } else {
  204. auto &errors = std::get<std::vector<scripting::script_error>>(res);
  205. for (auto &line: errors) {
  206. std::cout << line.message << "\n at line ";
  207. if(line.location) {
  208. std::cout << line.location->line_number << ":"
  209. << line.location->column_number << "\n";
  210. std::cout << " " << *line.location->line_contents << "\n";
  211. std::cout << " " << std::string(line.location->column_number - 1, ' ') << "^\n";
  212. } else std::cout << "UNKNOWN\n";
  213. }
  214. }
  215. }
  216. }
  217. void exec(std::span<std::string_view> args) {
  218. //std::vector<decltype(scripting::prepare_interpreter(std::string{}))> batch;
  219. std::ifstream src_str(std::string{args.front()});
  220. std::stringstream code;
  221. code << src_str.rdbuf();
  222. std::string code_val = code.str();
  223. auto engine = scripting::prepare_interpreter(std::string{});
  224. bool exit = false;
  225. constexpr scripting::UserScriptLibraryParameters params{};
  226. engine = scripting::register_array_lib(std::move(engine), params);
  227. engine = scripting::register_crypto_lib(std::move(engine), params);
  228. engine = scripting::register_utils_lib(std::move(engine), params);
  229. engine->registerFunction("exit", std::make_unique<fn_exit>(exit));
  230. engine->registerFunction("print", std::make_unique<print>(std::cout));
  231. auto errors = engine->prepare(code_val);
  232. if(not errors.empty()) {
  233. for (auto &line: errors) {
  234. std::cout << line.message << "\n at line ";
  235. if(line.location) {
  236. std::cout << line.location->line_number << ":"
  237. << line.location->column_number << "\n";
  238. std::cout << " " << *line.location->line_contents << "\n";
  239. std::cout << " " << std::string(line.location->column_number - 1, ' ') << "^\n";
  240. } else std::cout << "UNKNOWN\n";
  241. }
  242. return;
  243. }
  244. while (not exit) {
  245. auto res = engine->stepOnce();
  246. if (not res) {
  247. } else {
  248. auto line = res.value();
  249. std::cout << line.message << "\n at line ";
  250. if(line.location) {
  251. std::cout << line.location->line_number << ":"
  252. << line.location->column_number << "\n";
  253. std::cout << " " << *line.location->line_contents << "\n";
  254. std::cout << " " << std::string(line.location->column_number - 1, ' ') << "^\n";
  255. } else std::cout << "UNKNOWN\n";
  256. }
  257. }
  258. }
  259. #if defined(__linux__) or defined(WIN32)
  260. constexpr bool trim_first_argument = true;
  261. #else
  262. constexpr bool trim_first_argument = false;
  263. static_assert(false, "Undefined status of the first argument");
  264. #endif
  265. int cpp_main(std::span<std::string_view> args) {
  266. if constexpr (trim_first_argument) {
  267. args = args.subspan(1);
  268. }
  269. if(args.empty() || args.front() == "immediate") {
  270. immediate_interactive();
  271. std::exit(0);
  272. } else if(args.front() == "compare") {
  273. args = args.subspan(1);
  274. if(args.size() != 2) {
  275. std::cerr << "compare expects 2 files as arguments" << std::endl;
  276. std::terminate();
  277. }
  278. } else if(args.front() == "bench_exec") {
  279. args = args.subspan(1);
  280. if(args.size() > 1) {
  281. std::cerr << "bench_exec expects 0 or 1 file as arguments" << std::endl;
  282. std::terminate();
  283. }
  284. if(args.empty()) process_bench();
  285. else process_bench(std::string{args.front()});
  286. } else if(args.front() == "bench_compile") {
  287. args = args.subspan(1);
  288. if(args.size() > 1) {
  289. std::cerr << "bench_compile expects 0 or 1 file as arguments" << std::endl;
  290. std::terminate();
  291. }
  292. if(args.empty()) compile_bench();
  293. else compile_bench(std::string{args.front()});
  294. } else if(args.front() == "exec") {
  295. exec(args.subspan(1));
  296. } else {
  297. std::cerr << "Unknown option" << std::endl;
  298. }
  299. return 0;
  300. }
  301. int main(int argc, char** argv) {
  302. std::vector<std::string_view> args;
  303. for(auto& arg : std::span(argv, argv+argc)) {
  304. args.emplace_back(arg, arg+strlen(arg));
  305. }
  306. return cpp_main(args);
  307. }