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.

373 lines
13 KiB

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