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.

677 lines
26 KiB

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