HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
formula_parser.hpp
1// Copyright Take Vos 2020-2021.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "formula_node.hpp"
8#include "formula_add_node.hpp"
9#include "formula_arguments.hpp"
10#include "formula_assign_node.hpp"
11#include "formula_binary_operator_node.hpp"
12#include "formula_bit_and_node.hpp"
13#include "formula_bit_or_node.hpp"
14#include "formula_bit_xor_node.hpp"
15#include "formula_call_node.hpp"
16#include "formula_decrement_node.hpp"
17#include "formula_div_node.hpp"
18#include "formula_eq_node.hpp"
19#include "formula_evaluation_context.hpp"
20#include "formula_filter_node.hpp"
21#include "formula_ge_node.hpp"
22#include "formula_gt_node.hpp"
23#include "formula_increment_node.hpp"
24#include "formula_index_node.hpp"
25#include "formula_inplace_add_node.hpp"
26#include "formula_inplace_and_node.hpp"
27#include "formula_inplace_div_node.hpp"
28#include "formula_inplace_mod_node.hpp"
29#include "formula_inplace_mul_node.hpp"
30#include "formula_inplace_or_node.hpp"
31#include "formula_inplace_shl_node.hpp"
32#include "formula_inplace_shr_node.hpp"
33#include "formula_inplace_sub_node.hpp"
34#include "formula_inplace_xor_node.hpp"
35#include "formula_invert_node.hpp"
36#include "formula_le_node.hpp"
37#include "formula_literal_node.hpp"
38#include "formula_logical_and_node.hpp"
39#include "formula_logical_not_node.hpp"
40#include "formula_logical_or_node.hpp"
41#include "formula_lt_node.hpp"
42#include "formula_map_literal_node.hpp"
43#include "formula_member_node.hpp"
44#include "formula_minus_node.hpp"
45#include "formula_mod_node.hpp"
46#include "formula_mul_node.hpp"
47#include "formula_name_node.hpp"
48#include "formula_ne_node.hpp"
49#include "formula_plus_node.hpp"
50#include "formula_pow_node.hpp"
51#include "formula_shl_node.hpp"
52#include "formula_shr_node.hpp"
53#include "formula_sub_node.hpp"
54#include "formula_ternary_operator_node.hpp"
55#include "formula_unary_operator_node.hpp"
56#include "formula_vector_literal_node.hpp"
57#include "formula_post_process_context.hpp"
58#include "../macros.hpp"
59#include <string>
60#include <string_view>
61#include <memory>
62
63hi_export_module(hikogui.formula.formula_parser);
64
65hi_export namespace hi { inline namespace v1 {
66
67template<std::input_iterator It, std::sentinel_for<It> ItEnd>
69
70template<std::input_iterator It, std::sentinel_for<It> ItEnd>
72
73[[nodiscard]] constexpr std::pair<uint8_t, bool> operator_precedence(token const& op, bool binary) noexcept
74{
75 if (op != token::other) {
76 return {uint8_t{0}, false};
77 } else {
78 hilet[precedence, left_to_right] = operator_precedence(static_cast<std::string>(op), binary);
79 return {static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() - precedence), left_to_right};
80 }
81}
82
84parse_operation_formula(std::unique_ptr<formula_node> lhs, token const& op, std::unique_ptr<formula_node> rhs)
85{
86 if (lhs) {
87 // Binary operator
88 switch (operator_to_int(static_cast<std::string>(op))) {
89 case operator_to_int("."):
90 return std::make_unique<formula_member_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
91 case operator_to_int("**"):
92 return std::make_unique<formula_pow_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
93 case operator_to_int("*"):
94 return std::make_unique<formula_mul_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
95 case operator_to_int("/"):
96 return std::make_unique<formula_div_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
97 case operator_to_int("%"):
98 return std::make_unique<formula_mod_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
99 case operator_to_int("+"):
100 return std::make_unique<formula_add_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
101 case operator_to_int("-"):
102 return std::make_unique<formula_sub_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
103 case operator_to_int("<<"):
104 return std::make_unique<formula_shl_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
105 case operator_to_int(">>"):
106 return std::make_unique<formula_shr_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
107 case operator_to_int("<"):
108 return std::make_unique<formula_lt_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
109 case operator_to_int(">"):
110 return std::make_unique<formula_gt_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
111 case operator_to_int("<="):
112 return std::make_unique<formula_le_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
113 case operator_to_int(">="):
114 return std::make_unique<formula_ge_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
115 case operator_to_int("=="):
116 return std::make_unique<formula_eq_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
117 case operator_to_int("!="):
118 return std::make_unique<formula_ne_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
119 case operator_to_int("&"):
120 return std::make_unique<formula_bit_and_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
121 case operator_to_int("^"):
122 return std::make_unique<formula_bit_xor_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
123 case operator_to_int("|"):
124 return std::make_unique<formula_bit_or_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
125 case operator_to_int("&&"):
126 return std::make_unique<formula_logical_and_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
127 case operator_to_int("||"):
128 return std::make_unique<formula_logical_or_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
129 case operator_to_int("?"):
130 return std::make_unique<formula_ternary_operator_node>(op.line_nr, op.column_nr, std::move(lhs), *rhs);
131 case operator_to_int("["):
132 return std::make_unique<formula_index_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
133 case operator_to_int("("):
134 return std::make_unique<formula_call_node>(op.line_nr, op.column_nr, std::move(lhs), *rhs);
135 case operator_to_int("="):
136 return std::make_unique<formula_assign_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
137 case operator_to_int("+="):
138 return std::make_unique<formula_inplace_add_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
139 case operator_to_int("-="):
140 return std::make_unique<formula_inplace_sub_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
141 case operator_to_int("*="):
142 return std::make_unique<formula_inplace_mul_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
143 case operator_to_int("/="):
144 return std::make_unique<formula_inplace_div_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
145 case operator_to_int("%="):
146 return std::make_unique<formula_inplace_mod_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
147 case operator_to_int("<<="):
148 return std::make_unique<formula_inplace_shl_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
149 case operator_to_int(">>="):
150 return std::make_unique<formula_inplace_shr_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
151 case operator_to_int("&="):
152 return std::make_unique<formula_inplace_and_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
153 case operator_to_int("|="):
154 return std::make_unique<formula_inplace_or_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
155 case operator_to_int("^="):
156 return std::make_unique<formula_inplace_xor_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
157 case operator_to_int("!"):
158 return std::make_unique<formula_filter_node>(op.line_nr, op.column_nr, std::move(lhs), std::move(rhs));
159 default:
160 throw parse_error(std::format("{}: Unexpected binary operator {}.", op.line_nr, op.column_nr, op));
161 }
162 } else {
163 // Unary operator
164 switch (operator_to_int(static_cast<std::string>(op))) {
165 case operator_to_int("+"):
166 return std::make_unique<formula_plus_node>(op.line_nr, op.column_nr, std::move(rhs));
167 case operator_to_int("-"):
168 return std::make_unique<formula_minus_node>(op.line_nr, op.column_nr, std::move(rhs));
169 case operator_to_int("~"):
170 return std::make_unique<formula_invert_node>(op.line_nr, op.column_nr, std::move(rhs));
171 case operator_to_int("!"):
172 return std::make_unique<formula_logical_not_node>(op.line_nr, op.column_nr, std::move(rhs));
173 case operator_to_int("++"):
174 return std::make_unique<formula_increment_node>(op.line_nr, op.column_nr, std::move(rhs));
175 case operator_to_int("--"):
176 return std::make_unique<formula_decrement_node>(op.line_nr, op.column_nr, std::move(rhs));
177 default:
178 throw parse_error(std::format("{}:{}: Unexpected unary operator {}.", op.line_nr, op.column_nr, op));
179 }
180 }
181}
182
192template<std::input_iterator It, std::sentinel_for<It> ItEnd>
194{
195 hi_assert(it != last);
196 hilet line_nr = it->line_nr;
197 hilet column_nr = it->column_nr;
198
199 if (*it == token::integer) {
200 return std::make_unique<formula_literal_node>(line_nr, column_nr, datum{static_cast<long long>(*it++)});
201
202 } else if (*it == token::real) {
203 return std::make_unique<formula_literal_node>(line_nr, column_nr, datum{static_cast<double>(*it++)});
204
205 } else if (*it == token::dstr) {
206 return std::make_unique<formula_literal_node>(line_nr, column_nr, datum{static_cast<std::string>(*it++)});
207
208 } else if (*it == token::id and *it == "true") {
209 ++it;
210 return std::make_unique<formula_literal_node>(line_nr, column_nr, datum{true});
211
212 } else if (*it == token::id and *it == "false") {
213 ++it;
214 return std::make_unique<formula_literal_node>(line_nr, column_nr, datum{false});
215
216 } else if (*it == token::id and *it == "null") {
217 ++it;
218 return std::make_unique<formula_literal_node>(line_nr, column_nr, datum{nullptr});
219
220 } else if (*it == token::id and *it == "undefined") {
221 ++it;
222 return std::make_unique<formula_literal_node>(line_nr, column_nr, datum{});
223
224 } else if (*it == token::id) {
225 return std::make_unique<formula_name_node>(line_nr, column_nr, static_cast<std::string>(*it++));
226
227 } else if (*it == '(') {
228 ++it;
229 auto subformula = parse_formula(it, last);
230
231 if (*it == ')') {
232 ++it;
233 } else {
234 throw parse_error(std::format("{}:{}: Expected ')' token for function call got {}.", line_nr, column_nr, *it));
235 }
236
237 return subformula;
238
239 } else if (*it == '[') {
240 ++it;
241
242 formula_node::formula_vector values;
243
244 // ',' is between each formula, but a ']' may follow a ',' directly.
245 while (*it != ']') {
246 values.push_back(parse_formula(it, last));
247
248 if (*it == ',') {
249 ++it;
250 } else if (*it == ']') {
251 ++it;
252 break;
253 } else {
254 throw parse_error(
255 std::format("{}:{}: Expected ']' or ',' after a vector sub-formula. got {}", line_nr, column_nr, *it));
256 }
257 }
258
259 return std::make_unique<formula_vector_literal_node>(line_nr, column_nr, std::move(values));
260
261 } else if (*it == '{') {
262 ++it;
263
264 formula_node::formula_vector keys;
265 formula_node::formula_vector values;
266
267 // ',' is between each formula, but a ']' may follow a ',' directly.
268 while (*it != '}') {
269 keys.push_back(parse_formula(it, last));
270
271 if (*it == ':') {
272 ++it;
273 } else {
274 throw parse_error(std::format("{}:{}: Expected ':' after a map key. got {}", line_nr, column_nr, *it));
275 }
276
277 values.push_back(parse_formula(it, last));
278
279 if (*it == ',') {
280 ++it;
281 } else if (*it == '}') {
282 ++it;
283 break;
284 } else {
285 throw parse_error(
286 std::format("{}:{}: Expected ']' or ',' after a vector sub-formula. got {}.", line_nr, column_nr, *it));
287 }
288 }
289
290 return std::make_unique<formula_map_literal_node>(line_nr, column_nr, std::move(keys), std::move(values));
291
292 } else if (*it == token::other) {
293 hilet unary_op = *it++;
294 hilet[precedence, left_to_right] = operator_precedence(unary_op, false);
297 return parse_operation_formula({}, unary_op, std::move(sub_expression));
298
299 } else {
300 throw parse_error(std::format("{}:{}: Unexpected token in primary formula {}.", line_nr, column_nr, *it));
301 }
302}
303
306template<std::input_iterator It, std::sentinel_for<It> ItEnd>
308{
309 hi_assert(it != last);
310
311 auto rhs = parse_formula(it, last);
312
313 if (*it == ']') {
314 ++it;
315 } else {
316 throw parse_error(std::format("{}:{}: Expected ']' token at end of indexing operator got {}.", it->line_nr, it->column_nr, *it));
317 }
318 return rhs;
319}
320
323template<std::input_iterator It, std::sentinel_for<It> ItEnd>
325{
326 hi_assert(it != last);
327
328 auto rhs_true = parse_formula(it, last);
329
330 if (*it == ':') {
331 ++it;
332 } else {
333 throw parse_error(std::format("{}:{}: Expected ':' token in ternary formula {}.", it->line_nr, it->column_nr, *it));
334 }
335
336 auto rhs_false = parse_formula(it, last);
337
338 return std::make_unique<formula_arguments>(it->line_nr, it->column_nr, std::move(rhs_true), std::move(rhs_false));
339}
340
343template<std::input_iterator It, std::sentinel_for<It> ItEnd>
345{
346 hi_assert(it != last);
347
348 formula_node::formula_vector args;
349
350 if (*it == ')') {
351 ++it;
352
353 } else
354 while (true) {
355 args.push_back(parse_formula(it, last));
356
357 if (*it == ',') {
358 ++it;
359 continue;
360
361 } else if (*it == ')') {
362 ++it;
363 break;
364
365 } else {
366 throw parse_error(
367 std::format("{}:{}: Expected ',' or ')' After a function argument {}.", it->line_nr, it->column_nr, *it));
368 }
369 }
370
371 return std::make_unique<formula_arguments>(it->line_nr, it->column_nr, std::move(args));
372}
373
374template<std::input_iterator It, std::sentinel_for<It> ItEnd>
375[[nodiscard]] bool parse_formula_is_at_end(It& it, ItEnd last)
376{
377 if (it == last) {
378 return true;
379 }
380
381 if (*it != token::other) {
382 throw parse_error(std::format("{}:{}: Expecting an operator token got {}.", it->line_nr, it->column_nr, *it));
383 }
384
385 return *it == ')' or *it == '}' or *it == ']' or *it == ':' or *it == ',';
386}
387
392template<std::input_iterator It, std::sentinel_for<It> ItEnd>
394{
395 auto lookahead = token{};
396 auto lookahead_precedence = uint8_t{};
397 auto lookahead_left_to_right = false;
398
399 std::tie(lookahead_precedence, lookahead_left_to_right) = operator_precedence(lookahead = *it, true);
400 if (parse_formula_is_at_end(it, last)) {
401 return lhs;
402 }
403
405 hilet op = lookahead;
407 ++it;
408
410 if (op == '[') {
411 rhs = parse_index_formula(it, last);
412 } else if (op == '(') {
413 rhs = parse_call_argument_formula(it, last);
414 } else if (op == '?') {
416 } else {
417 rhs = parse_primary_formula(it, last);
418 }
419
420 std::tie(lookahead_precedence, lookahead_left_to_right) = operator_precedence(lookahead = *it, true);
421 if (parse_formula_is_at_end(it, last)) {
422 return parse_operation_formula(std::move(lhs), op, std::move(rhs));
423 }
424
428
429 std::tie(lookahead_precedence, lookahead_left_to_right) = operator_precedence(lookahead = *it, true);
430 if (parse_formula_is_at_end(it, last)) {
431 return parse_operation_formula(std::move(lhs), op, std::move(rhs));
432 }
433 }
434 lhs = parse_operation_formula(std::move(lhs), op, std::move(rhs));
435 }
436
437 return lhs;
438}
439
443template<std::input_iterator It, std::sentinel_for<It> ItEnd>
449
454{
455 auto token_it = lexer<lexer_config::c_style()>.parse(text.begin(), text.end());
456 return parse_formula(token_it, std::default_sentinel);
457}
458
462[[nodiscard]] inline std::unique_ptr<formula_node> parse_formula(std::string_view text, formula_post_process_context post_process_context = {})
463{
465 e->post_process(post_process_context);
466 return e;
467}
468
476[[nodiscard]] constexpr std::string_view::const_iterator find_end_of_formula(
477 std::string_view::const_iterator first,
478 std::string_view::const_iterator last,
479 std::string_view terminating_string) noexcept
480{
482 char in_string = 0;
483 bool in_escape = false;
484
485 for (auto i = first; i != last; i++) {
486 if (in_escape) {
487 in_escape = false;
488
489 } else if (in_string) {
490 if (*i == in_string) {
491 in_string = 0;
492 } else if (*i == '\\') {
493 in_escape = true;
494 }
495
496 } else {
497 switch (*i) {
498 case '"':
499 in_string = '"';
500 break;
501 case '\'':
502 in_string = '\'';
503 break;
504 case '{':
505 bracket_stack += '}';
506 break;
507 case '[':
508 bracket_stack += ']';
509 break;
510 case '(':
511 bracket_stack += ')';
512 break;
513 case '\\':
514 in_escape = true;
515 break; // It is possible to escape any character, including the terminating_character.
516 default:
517 if (bracket_stack.size() > 0) {
518 if (*i == bracket_stack.back()) {
520 }
521
522 } else if (std::string_view{i, last}.starts_with(terminating_string)) {
523 return i;
524 }
525 }
526 }
527 }
528 return last;
529}
530
531}} // namespace hi::inline v1
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:16
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
std::unique_ptr< formula_node > parse_index_formula(It &it, ItEnd last)
Parse the rhs of an index operator, including the closing bracket.
Definition formula_parser.hpp:307
std::unique_ptr< formula_node > parse_ternary_argument_formula(It &it, ItEnd last)
Parse the rhs of an index operator, including the closing bracket.
Definition formula_parser.hpp:324
std::unique_ptr< formula_node > parse_formula_1(It &it, ItEnd last, std::unique_ptr< formula_node > lhs, uint8_t min_precedence)
Parse an formula.
Definition formula_parser.hpp:393
std::unique_ptr< formula_node > parse_call_argument_formula(It &it, ItEnd last)
Parse the rhs of an index operator, including the closing bracket.
Definition formula_parser.hpp:344
constexpr std::string_view::const_iterator find_end_of_formula(std::string_view::const_iterator first, std::string_view::const_iterator last, std::string_view terminating_string) noexcept
Find the end of an formula.
Definition formula_parser.hpp:476
std::unique_ptr< formula_node > parse_primary_formula(It &it, ItEnd last)
Parse a lhs or rhs part of an formula.
Definition formula_parser.hpp:193
std::unique_ptr< formula_node > parse_formula_without_post_processing(std::string_view text)
Parse an formula.
Definition formula_parser.hpp:453
std::unique_ptr< formula_node > parse_formula(It &it, ItEnd last)
Parse an formula.
Definition formula_parser.hpp:444
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
A dynamic data type.
Definition datum.hpp:212
Definition formula_post_process_context.hpp:237
Definition token.hpp:15
Exception thrown during parsing on an error.
Definition exception_intf.hpp:47
T max(T... args)
T move(T... args)
T pop_back(T... args)
T tie(T... args)