Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

684 lignes
26 KiB

  1. #include <map>
  2. #include <optional>
  3. #include <sstream>
  4. #include <iostream>
  5. #include <array>
  6. #include <charconv>
  7. #include <utility>
  8. #include <algorithm>
  9. #include <limits>
  10. #include "UserScript/parser.h"
  11. #include "UserScript.h"
  12. //////////////////////
  13. /// PARSER HELPERS ///
  14. //////////////////////
  15. using token = scripting::ast::token;
  16. using symbol_t = scripting::ast::symbol_t;
  17. template<typename T>
  18. struct parse_result {
  19. std::optional<T> result;
  20. std::span<token> rest;
  21. operator bool() { return result.has_value(); }
  22. };
  23. bool next_is_newline(std::span<token> current) {
  24. return (not current.empty()) && (holds_alternative<symbol_t>(current.front().value) && (get<symbol_t>(current.front().value) == symbol_t::new_line));
  25. }
  26. std::span<token> trim_newline(std::span<token> current) {
  27. while(next_is_newline(current)) {
  28. current = current.subspan(1);
  29. }
  30. return current;
  31. }
  32. ///////////////////////////
  33. /// PARSER DECLARATIONS ///
  34. ///////////////////////////
  35. parse_result<scripting::ast::expression> try_expression(std::span<token> code, std::vector<scripting::script_error>& errors);
  36. parse_result<scripting::ast::binary_algebraic_expression> try_binary_algebraic_expression(std::span<token> code, std::vector<scripting::script_error>& errors);
  37. parse_result<scripting::ast::variable_expression> try_variable_expression(std::span<token> code, std::vector<scripting::script_error>& errors);
  38. parse_result<scripting::ast::literal_int_expression> try_literal_int_expression(std::span<token> code, std::vector<scripting::script_error>& errors);
  39. parse_result<scripting::ast::literal_string_expression> try_literal_string_expression(std::span<token> code, std::vector<scripting::script_error>& errors);
  40. parse_result<scripting::ast::statement> try_statement(std::span<token> code, std::vector<scripting::script_error>& errors);
  41. parse_result<scripting::ast::command_expression> try_command_expr(std::span<token> code, std::vector<scripting::script_error>& errors);
  42. parse_result<scripting::ast::conditional> try_conditional(const std::span<token> code, std::vector<scripting::script_error>& errors, const std::string_view initiator = "if");
  43. parse_result<scripting::ast::while_loop> try_while_loop(const std::span<token> code, std::vector<scripting::script_error>& errors);
  44. parse_result<scripting::ast::paren_expression> try_paren_expression(std::span<token> code, std::vector<scripting::script_error>& errors);
  45. parse_result<scripting::ast::unary_algebraic_expression> try_unary_algebraic_expression(std::span<token> code, std::vector<scripting::script_error>& errors);
  46. //////////////////////////
  47. /// PARSER DEFINITIONS ///
  48. //////////////////////////
  49. parse_result<scripting::ast::command_expression> try_command_expr(std::span<token> code, std::vector<scripting::script_error>& errors) {
  50. scripting::ast::command_expression cmd;
  51. auto current = code;
  52. if(current.empty()) return {std::nullopt, code};
  53. if(not holds_alternative<symbol_t>(current.front().value)) return {std::nullopt, code};
  54. if(get<symbol_t>(current.front().value) != symbol_t::divide) return {std::nullopt, code};
  55. cmd.location = current.front().location;
  56. current = current.subspan(1);
  57. if(current.empty()) {
  58. errors.push_back(
  59. scripting::script_error{.location = code.front().location, .message = "Expected command name"});
  60. return {std::nullopt, code};
  61. }
  62. if(not holds_alternative<scripting::ast::identifier>(current.front().value)) {
  63. errors.push_back(
  64. scripting::script_error{.location = current.front().location, .message = "Expected command name"});
  65. return {std::nullopt, code};
  66. }
  67. cmd.name = get<scripting::ast::identifier>(current.front().value);
  68. current = current.subspan(1);
  69. while(
  70. not current.empty()
  71. and not (
  72. holds_alternative<symbol_t>(current.front().value)
  73. and get<symbol_t>(current.front().value) == symbol_t::new_line
  74. )
  75. and not (
  76. holds_alternative<symbol_t>(current.front().value)
  77. and get<symbol_t>(current.front().value) == symbol_t::r_paren
  78. )
  79. ) {
  80. auto [expr, rest] = try_expression(current, errors);
  81. if(not expr) {
  82. errors.push_back(
  83. scripting::script_error{.location = current.front().location, .message = "Expected expression"});
  84. return {std::nullopt, code};
  85. }
  86. cmd.arguments.push_back(std::make_unique<scripting::ast::expression>(std::move(expr.value())));
  87. current = rest;
  88. }
  89. return {std::move(cmd), current};
  90. }
  91. parse_result<scripting::ast::expression> try_expression(std::span<token> code, std::vector<scripting::script_error>& errors) {
  92. scripting::ast::expression node;
  93. auto current = code;
  94. #ifdef HANDLE_EXPRESSION
  95. static_assert(false, "Found a macro name HANDLE_EXPRESSION, halting");
  96. #endif
  97. #define HANDLE_EXPRESSION(type) \
  98. node = scripting::ast::expression{ \
  99. .location = current.front().location, \
  100. .contents = std::make_unique<type>(std::move(expr.value())) \
  101. }; \
  102. current = rest;
  103. if(auto [expr, rest] = try_binary_algebraic_expression(current, errors); expr) {
  104. HANDLE_EXPRESSION(scripting::ast::binary_algebraic_expression)
  105. } else if(auto [expr, rest] = try_literal_string_expression(current, errors); expr) {
  106. HANDLE_EXPRESSION(scripting::ast::literal_string_expression)
  107. } else if(auto [expr, rest] = try_literal_int_expression(current, errors); expr) {
  108. HANDLE_EXPRESSION(scripting::ast::literal_int_expression)
  109. } else if(auto [expr, rest] = try_unary_algebraic_expression(current, errors); expr) {
  110. HANDLE_EXPRESSION(scripting::ast::unary_algebraic_expression)
  111. } else if(auto [expr, rest] = try_paren_expression(current, errors); expr) {
  112. HANDLE_EXPRESSION(scripting::ast::paren_expression)
  113. } else if(auto [expr, rest] = try_variable_expression(current, errors); expr) {
  114. HANDLE_EXPRESSION(scripting::ast::variable_expression)
  115. } else {
  116. return {std::nullopt, code};
  117. }
  118. #undef HANDLE_EXPRESSION
  119. return {.result = std::move(node), .rest = current};
  120. }
  121. parse_result<scripting::ast::conditional> try_conditional(const std::span<token> code, std::vector<scripting::script_error>& errors, const std::string_view initiator) {
  122. #ifdef FAILURE
  123. static_assert(false, "Found a macro name FAILURE, halting");
  124. #endif
  125. #define FAILURE {.result = std::nullopt, .rest = code}
  126. scripting::ast::conditional result{};
  127. if(code.empty()) return FAILURE;
  128. auto current = code;
  129. result.location = current.front().location;
  130. const auto endif_found = [&]() -> bool {
  131. if (current.empty()) return false;
  132. if (not holds_alternative<scripting::ast::identifier>(current.front().value)) return false;
  133. if (get<scripting::ast::identifier>(current.front().value).value != "endif") return false;
  134. return true;
  135. };
  136. // chomps a new line if available, returns true if it failed
  137. const auto MISSING_NEW_LINE = [&]() -> bool {
  138. if (current.empty()) {
  139. errors.push_back(
  140. scripting::script_error{.location = code.front().location, .message = "Interrupted conditional"});
  141. return true;
  142. }
  143. auto new_line = current.front();
  144. if (not holds_alternative<symbol_t>(current.front().value)) {
  145. errors.push_back(
  146. scripting::script_error{.location = current.front().location, .message = "Expected new line"});
  147. return true;
  148. }
  149. if (get<symbol_t>(current.front().value) != symbol_t::new_line) {
  150. errors.push_back(
  151. scripting::script_error{.location = current.front().location, .message = "Expected new line"});
  152. return true;
  153. }
  154. current = current.subspan(1);
  155. return false;
  156. };
  157. if (current.empty()) return FAILURE;
  158. if (not holds_alternative<scripting::ast::identifier>(current.front().value)) return FAILURE;
  159. if (get<scripting::ast::identifier>(current.front().value).value != initiator) return FAILURE;
  160. current = current.subspan(1);
  161. // Read the condition
  162. auto conditional_node = try_expression(current, errors);
  163. if (not conditional_node.result) {
  164. errors.push_back(
  165. scripting::script_error{.location = code.front().location, .message = "Expected expression in conditional"});
  166. return FAILURE;
  167. }
  168. result.condition = std::make_unique<scripting::ast::expression>(std::move(conditional_node.result.value()));
  169. current = conditional_node.rest;
  170. if(MISSING_NEW_LINE()) return FAILURE;
  171. if (current.empty()) {
  172. errors.push_back(
  173. scripting::script_error{.location = code.front().location, .message = "Interrupted conditional"});
  174. return FAILURE;
  175. }
  176. result.on_condition = std::make_unique<scripting::ast::block>();
  177. result.on_condition->location = current.front().location;
  178. bool else_mode = false;
  179. while (not endif_found()) {
  180. const auto error_count = errors.size();
  181. // Handles elseif sequences as nested ifs in the else block
  182. auto nester = try_conditional(current, errors, "elseif");
  183. if(nester.result) {
  184. result.otherwise = std::make_unique<scripting::ast::block>();
  185. result.otherwise->location = current.front().location;
  186. result.otherwise->contents.push_back(scripting::ast::statement{
  187. .location = current.front().location,
  188. .contents = std::make_unique<scripting::ast::conditional>(std::move(nester.result.value())),
  189. });
  190. current = nester.rest;
  191. return {.result = std::move(result), .rest = current};
  192. } else if(error_count != errors.size()) {
  193. return FAILURE;
  194. }
  195. // Handles code
  196. if(auto block_contents = try_statement(current, errors); block_contents.result) {
  197. if(not else_mode) {
  198. result.on_condition->contents.push_back(std::move(block_contents.result.value()));
  199. } else {
  200. result.otherwise->contents.push_back(std::move(block_contents.result.value()));
  201. }
  202. current = block_contents.rest;
  203. } else {
  204. if(current.empty()) {
  205. errors.push_back(
  206. scripting::script_error{.location = code.front().location, .message = "Interrupted conditional"});
  207. return FAILURE;
  208. }
  209. // Handles switching to else mode
  210. if(
  211. holds_alternative<scripting::ast::identifier>(current.front().value)
  212. && get<scripting::ast::identifier>(current.front().value).value == "else"
  213. ) {
  214. auto loc = current.front().location;
  215. current = current.subspan(1);
  216. if(MISSING_NEW_LINE()) return FAILURE;
  217. if(else_mode) {
  218. errors.push_back(
  219. scripting::script_error{.location = current.front().location, .message = "Repeated else in conditional"});
  220. } else {
  221. else_mode = true;
  222. result.otherwise = std::make_unique<scripting::ast::block>();
  223. result.otherwise->location = std::move(loc);
  224. }
  225. }else {
  226. errors.push_back(
  227. scripting::script_error{.location = current.front().location, .message = "Unexpected expression content"});
  228. return FAILURE;
  229. }
  230. }
  231. }
  232. // checks for endif
  233. if(endif_found()) {
  234. current = current.subspan(1);
  235. if(not current.empty() && MISSING_NEW_LINE()) return FAILURE;
  236. }
  237. return {.result = std::move(result), .rest = current};
  238. #undef FAILURE
  239. }
  240. parse_result<scripting::ast::while_loop> try_while_loop(const std::span<token> code, std::vector<scripting::script_error>& errors) {
  241. #ifdef FAILURE
  242. static_assert(false, "Found a macro name FAILURE, halting");
  243. #endif
  244. #define FAILURE {.result = std::nullopt, .rest = code}
  245. scripting::ast::while_loop result{};
  246. if(code.empty()) return FAILURE;
  247. auto current = code;
  248. result.location = current.front().location;
  249. const auto endwhile_found = [&]() -> bool {
  250. if (current.empty()) return false;
  251. if (not holds_alternative<scripting::ast::identifier>(current.front().value)) return false;
  252. if (get<scripting::ast::identifier>(current.front().value).value != "endwhile") return false;
  253. return true;
  254. };
  255. // chomps a new line if available, returns true if it failed
  256. const auto MISSING_NEW_LINE = [&]() -> bool {
  257. if (current.empty()) {
  258. errors.push_back(
  259. scripting::script_error{.location = code.front().location, .message = "Interrupted while loop"});
  260. return true;
  261. }
  262. auto new_line = current.front();
  263. if (not holds_alternative<symbol_t>(current.front().value)) {
  264. errors.push_back(
  265. scripting::script_error{.location = current.front().location, .message = "Expected new line"});
  266. return true;
  267. }
  268. if (get<symbol_t>(current.front().value) != symbol_t::new_line) {
  269. errors.push_back(
  270. scripting::script_error{.location = current.front().location, .message = "Expected new line"});
  271. return true;
  272. }
  273. current = current.subspan(1);
  274. return false;
  275. };
  276. if (current.empty()) return FAILURE;
  277. if (not holds_alternative<scripting::ast::identifier>(current.front().value)) return FAILURE;
  278. if (get<scripting::ast::identifier>(current.front().value).value != "while") return FAILURE;
  279. current = current.subspan(1);
  280. // Read the condition
  281. auto conditional_node = try_expression(current, errors);
  282. if (not conditional_node.result) {
  283. errors.push_back(
  284. scripting::script_error{.location = code.front().location, .message = "Expected expression in while loop"});
  285. return FAILURE;
  286. }
  287. result.condition = std::make_unique<scripting::ast::expression>(std::move(conditional_node.result.value()));
  288. current = conditional_node.rest;
  289. if(MISSING_NEW_LINE()) return FAILURE;
  290. if (current.empty()) {
  291. errors.push_back(
  292. scripting::script_error{.location = code.front().location, .message = "Interrupted while loop"});
  293. return FAILURE;
  294. }
  295. result.on_condition = std::make_unique<scripting::ast::block>();
  296. result.on_condition->location = current.front().location;
  297. while (not endwhile_found()) {
  298. const auto error_count = errors.size();
  299. if(error_count != errors.size()) {
  300. return FAILURE;
  301. }
  302. // Handles code
  303. if(auto block_contents = try_statement(current, errors); block_contents.result) {
  304. result.on_condition->contents.push_back(std::move(block_contents.result.value()));
  305. current = block_contents.rest;
  306. } else {
  307. if(current.empty()) {
  308. errors.push_back(
  309. scripting::script_error{.location = code.front().location, .message = "Interrupted while loop"});
  310. return FAILURE;
  311. } else {
  312. errors.push_back(
  313. scripting::script_error{.location = current.front().location, .message = "Unexpected statement in block"});
  314. return FAILURE;
  315. }
  316. }
  317. }
  318. // checks for endif
  319. if(endwhile_found()) {
  320. current = current.subspan(1);
  321. if(not current.empty() && MISSING_NEW_LINE()) return FAILURE;
  322. }
  323. return {.result = std::move(result), .rest = current};
  324. #undef FAILURE
  325. }
  326. parse_result<scripting::ast::paren_expression> try_paren_expression(std::span<token> code, std::vector<scripting::script_error>& errors) {
  327. scripting::ast::paren_expression paren;
  328. auto current = code;
  329. if(current.empty()) return {std::nullopt, code};
  330. if(not holds_alternative<symbol_t>(current.front().value)) return {std::nullopt, code};
  331. if(get<symbol_t>(current.front().value) != symbol_t::l_paren) return {std::nullopt, code};
  332. paren.location = current.front().location;
  333. current = current.subspan(1);
  334. if(auto [cmd, cmd_rest] = try_command_expr(current, errors); cmd) {
  335. paren.content = std::make_unique<scripting::ast::command_expression>(std::move(cmd.value()));
  336. current = cmd_rest;
  337. } else if(auto [expr, expr_rest] = try_expression(current, errors); expr) {
  338. paren.content = std::make_unique<scripting::ast::expression>(std::move(expr.value()));
  339. current = expr_rest;
  340. } else {
  341. errors.push_back(
  342. scripting::script_error{.location = (current.empty() ? paren.location : current.front().location), .message = "Expected either a command or some type of expression"});
  343. return {std::nullopt, code};
  344. }
  345. if(current.empty()) {
  346. errors.push_back(
  347. scripting::script_error{.location = paren.location, .message = "No matching parenthesis"});
  348. return {std::nullopt, code};
  349. }
  350. if(not holds_alternative<symbol_t>(current.front().value) or get<symbol_t>(current.front().value) != symbol_t::r_paren) {
  351. errors.push_back(
  352. scripting::script_error{.location = current.front().location, .message = "No matching parenthesis, expected a closing parenthesis"});
  353. return {std::nullopt, code};
  354. }
  355. current = current.subspan(1);
  356. return {.result = std::move(paren), .rest = current};
  357. }
  358. parse_result<scripting::ast::statement> try_statement(std::span<token> code, std::vector<scripting::script_error>& errors) {
  359. scripting::ast::statement node;
  360. auto current = code;
  361. current = trim_newline(current);
  362. if(auto [expr, rest] = try_conditional(current, errors); expr) {
  363. node.contents = std::make_unique<scripting::ast::conditional>(std::move(expr.value()));
  364. node.location = current.front().location;
  365. current = rest;
  366. } else if(auto [expr, rest] = try_command_expr(current, errors); expr) {
  367. node.contents = std::make_unique<scripting::ast::command_expression>(std::move(expr.value()));
  368. node.location = current.front().location;
  369. current = rest;
  370. } else if(auto [expr, rest] = try_while_loop(current, errors); expr) {
  371. node.contents = std::make_unique<scripting::ast::while_loop>(std::move(expr.value()));
  372. node.location = current.front().location;
  373. current = rest;
  374. } else return {std::nullopt, code};
  375. current = trim_newline(current);
  376. return {.result = std::move(node), .rest = current};
  377. }
  378. parse_result<scripting::ast::unary_algebraic_expression> try_unary_algebraic_expression(std::span<token> code, std::vector<scripting::script_error>& errors) {
  379. constexpr std::array lexer_operators = {symbol_t::binary_not, symbol_t::logical_not};
  380. constexpr std::array ast_operators = {scripting::ast::operator_t::binary_not, scripting::ast::operator_t::logical_not};
  381. static_assert(lexer_operators.size() == ast_operators.size());
  382. scripting::ast::unary_algebraic_expression node;
  383. auto current = code;
  384. if(current.empty()) return {std::nullopt, code};
  385. if(not holds_alternative<symbol_t>(current.front().value)) return {std::nullopt, code};
  386. auto res = std::ranges::find(lexer_operators, get<symbol_t>(current.front().value));
  387. if(res == lexer_operators.end()) return {std::nullopt, code};
  388. node.location = current.front().location;
  389. node.op = *(ast_operators.begin() + (res - lexer_operators.begin()));
  390. current = current.subspan(1);
  391. /// TODO: Gives the lowest priority to unaries (aka, they are applied last)
  392. auto operand = try_expression(current, errors);
  393. if (not operand.result) {
  394. errors.push_back(
  395. scripting::script_error{.location = code.front().location, .message = "Expected expression after unary operator"});
  396. return {std::nullopt, code};
  397. }
  398. node.content = std::make_unique<scripting::ast::expression>(std::move(operand.result.value()));
  399. current = operand.rest;
  400. return {.result = std::move(node), .rest = current};
  401. }
  402. parse_result<scripting::ast::binary_algebraic_expression> try_binary_algebraic_expression(std::span<token> code, std::vector<scripting::script_error>& errors) {
  403. // The two following arrays are the operator mappings
  404. constexpr std::array lexer_operators = {
  405. symbol_t::divide,
  406. symbol_t::modulo,
  407. symbol_t::multiply,
  408. symbol_t::subtract,
  409. symbol_t::add,
  410. symbol_t::bitshift_left,
  411. symbol_t::bitshift_right,
  412. symbol_t::rotate_left,
  413. symbol_t::rotate_right,
  414. symbol_t::less_than,
  415. symbol_t::greater_than,
  416. symbol_t::less_or_equal_than,
  417. symbol_t::greater_or_equal_than,
  418. symbol_t::equals,
  419. symbol_t::different,
  420. symbol_t::binary_and,
  421. symbol_t::binary_or,
  422. symbol_t::binary_xor,
  423. symbol_t::logical_and,
  424. symbol_t::logical_or,
  425. };
  426. constexpr std::array ast_operators = {
  427. scripting::ast::operator_t::divide,
  428. scripting::ast::operator_t::modulo,
  429. scripting::ast::operator_t::multiply,
  430. scripting::ast::operator_t::subtract,
  431. scripting::ast::operator_t::add,
  432. scripting::ast::operator_t::bitshift_left,
  433. scripting::ast::operator_t::bitshift_right,
  434. scripting::ast::operator_t::rotate_left,
  435. scripting::ast::operator_t::rotate_right,
  436. scripting::ast::operator_t::less_than,
  437. scripting::ast::operator_t::greater_than,
  438. scripting::ast::operator_t::less_or_equal_than,
  439. scripting::ast::operator_t::greater_or_equal_than,
  440. scripting::ast::operator_t::equals,
  441. scripting::ast::operator_t::different,
  442. scripting::ast::operator_t::binary_and,
  443. scripting::ast::operator_t::binary_or,
  444. scripting::ast::operator_t::binary_xor,
  445. scripting::ast::operator_t::logical_and,
  446. scripting::ast::operator_t::logical_or,
  447. };
  448. constexpr std::array ast_precedences = {
  449. 1,
  450. 1,
  451. 1,
  452. 2,
  453. 2,
  454. 3,
  455. 3,
  456. 3,
  457. 3,
  458. 4,
  459. 4,
  460. 4,
  461. 4,
  462. 4,
  463. 4,
  464. 5,
  465. 5,
  466. 5,
  467. 6,
  468. 7,
  469. };
  470. static_assert(lexer_operators.size() == ast_operators.size());
  471. static_assert(ast_precedences.size() == ast_operators.size());
  472. scripting::ast::binary_algebraic_expression node;
  473. auto current = code;
  474. #ifdef HANDLE_EXPRESSION
  475. static_assert(false, "Found a macro name HANDLE_EXPRESSION, halting");
  476. #endif
  477. #define HANDLE_EXPRESSION(type) \
  478. node.lhs = std::make_unique<scripting::ast::expression>(scripting::ast::expression{ \
  479. .location = current.front().location, \
  480. .contents = std::make_unique<type>(std::move(expr.value())) \
  481. }); \
  482. current = rest;
  483. if(auto [expr, rest] = try_literal_string_expression(current, errors); expr) {
  484. HANDLE_EXPRESSION(scripting::ast::literal_string_expression)
  485. } else if(auto [expr, rest] = try_literal_int_expression(current, errors); expr) {
  486. HANDLE_EXPRESSION(scripting::ast::literal_int_expression)
  487. } else if(auto [expr, rest] = try_unary_algebraic_expression(current, errors); expr) {
  488. HANDLE_EXPRESSION(scripting::ast::unary_algebraic_expression)
  489. } else if(auto [expr, rest] = try_paren_expression(current, errors); expr) {
  490. HANDLE_EXPRESSION(scripting::ast::paren_expression)
  491. } else if(auto [expr, rest] = try_variable_expression(current, errors); expr) {
  492. HANDLE_EXPRESSION(scripting::ast::variable_expression)
  493. } else {
  494. return {std::nullopt, code};
  495. }
  496. #undef HANDLE_EXPRESSION
  497. if(current.empty()) {
  498. return {std::nullopt, code};
  499. }
  500. if(not holds_alternative<symbol_t>(current.front().value)) {
  501. return {std::nullopt, code};
  502. }
  503. auto res = std::ranges::find(lexer_operators, get<symbol_t>(current.front().value));
  504. if(res == lexer_operators.end()) {
  505. return {std::nullopt, code};
  506. }
  507. auto rhs_idx = (res - lexer_operators.begin());
  508. node.op = *(ast_operators.begin() + rhs_idx);
  509. node.location = current.front().location;
  510. current = current.subspan(1);
  511. auto operand = try_expression(current, errors);
  512. if (not operand.result) {
  513. errors.push_back(
  514. scripting::script_error{.location = node.location, .message = "Expected expression after binary operator"});
  515. return {std::nullopt, code};
  516. }
  517. /// Check if our "large (rhs) bite" has an operation precedence that is bigger and in that case, swap the operations around
  518. //... We may need to do that iteratively until we risk a priority reversal. This is basically shifting the ast to the left until
  519. //... the precedence are "heapified" and left associative
  520. if(std::holds_alternative<std::unique_ptr<scripting::ast::binary_algebraic_expression>>(operand.result.value().contents)) {
  521. // Must check for precedence reordering
  522. auto& lhs = std::get<std::unique_ptr<scripting::ast::binary_algebraic_expression>>(operand.result.value().contents);
  523. auto lhs_it = std::ranges::find(ast_operators, lhs->op);
  524. auto lhs_idx = lhs_it - ast_operators.begin();
  525. // >= ensures left associativity
  526. if(ast_precedences[rhs_idx] <= ast_precedences[lhs_idx]) {
  527. // Precedence reordering required
  528. // https://link.excalidraw.com/l/hxPegpAmTX/6d1BYX0rfKU
  529. node.rhs = std::move(lhs->lhs);
  530. scripting::ast::binary_algebraic_expression reordered{
  531. .location = operand.result.value().location,
  532. .lhs = std::make_unique<scripting::ast::expression>(scripting::ast::expression{
  533. .location = node.location,
  534. .contents = std::make_unique<scripting::ast::binary_algebraic_expression>(std::move(node))
  535. }),
  536. .op = lhs->op,
  537. .rhs = std::move(lhs->rhs),
  538. };
  539. current = operand.rest;
  540. return {.result = std::move(reordered), .rest = current};
  541. }
  542. }
  543. // No reordering required
  544. node.rhs = std::make_unique<scripting::ast::expression>(std::move(operand.result.value()));
  545. current = operand.rest;
  546. return {.result = std::move(node), .rest = current};
  547. }
  548. parse_result<scripting::ast::variable_expression> try_variable_expression(std::span<token> code, std::vector<scripting::script_error>& errors) {
  549. scripting::ast::variable_expression node;
  550. auto current = code;
  551. if(current.empty()) return {std::nullopt, code};
  552. if(not holds_alternative<scripting::ast::identifier>(current.front().value)) return {std::nullopt, code};
  553. node.location = current.front().location;
  554. node.name = get<scripting::ast::identifier>(current.front().value);
  555. current = current.subspan(1);
  556. return {.result = std::move(node), .rest = current};
  557. }
  558. parse_result<scripting::ast::literal_string_expression> try_literal_string_expression(std::span<token> code, std::vector<scripting::script_error>& errors) {
  559. scripting::ast::literal_string_expression node;
  560. auto current = code;
  561. if(current.empty()) return {std::nullopt, code};
  562. if(not holds_alternative<std::string>(current.front().value)) return {std::nullopt, code};
  563. node.location = current.front().location;
  564. node.value = get<std::string>(current.front().value);
  565. current = current.subspan(1);
  566. return {.result = std::move(node), .rest = current};
  567. }
  568. parse_result<scripting::ast::literal_int_expression> try_literal_int_expression(std::span<token> code, std::vector<scripting::script_error>& errors) {
  569. scripting::ast::literal_int_expression node;
  570. auto current = code;
  571. if(current.empty()) return {std::nullopt, code};
  572. if(not holds_alternative<int32_t>(current.front().value)) return {std::nullopt, code};
  573. node.location = current.front().location;
  574. node.value = get<int32_t>(current.front().value);
  575. current = current.subspan(1);
  576. return {.result = std::move(node), .rest = current};
  577. }
  578. scripting::ast::block scripting::ast::parse(std::span<token> code, std::vector<scripting::script_error>& errors) {
  579. scripting::ast::block node;
  580. auto current = trim_newline(code);
  581. while(not current.empty()) {
  582. auto pre_size = current.size();
  583. auto [expr, rest] = try_statement(current, errors);
  584. if(expr) {
  585. node.contents.push_back(std::move(expr.value()));
  586. current = rest;
  587. } else {
  588. bool progress = false;
  589. while(not (next_is_newline(current) or current.empty())) {
  590. current = current.subspan(1);
  591. progress = true;
  592. }
  593. if(not progress && not errors.empty()) {
  594. return scripting::ast::block{};
  595. }
  596. if(not current.empty() && current.size() == pre_size) {
  597. errors.push_back(script_error{
  598. .location = current.front().location,
  599. .message = "Parsing stuck in infinite loop with no progress"
  600. });
  601. }
  602. }
  603. current = trim_newline(current);
  604. }
  605. if(not errors.empty()) {
  606. return scripting::ast::block{};
  607. }
  608. return std::move(node);
  609. }