OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
expression_def.hpp
Go to the documentation of this file.
1#include "expression.hpp"
2#include "error_handler.hpp"
3
4#include <cctype>
5#include <cstdlib>
6#include <iterator>
7#include <optional>
8
9namespace client { namespace parser
10{
11 template <typename Iterator>
17
18 template <typename Iterator>
19 void expression<Iterator>::skip_ws(Iterator& it, Iterator last) const
20 {
21 while (it != last) {
22 if (std::isspace(static_cast<unsigned char>(*it))) {
23 ++it;
24 } else if (*it == '/' && std::next(it) != last && *std::next(it) == '*') {
25 std::advance(it, 2);
26 while (it != last && !(*it == '*' && std::next(it) != last && *std::next(it) == '/')) {
27 ++it;
28 }
29 if (it != last) {
30 std::advance(it, 2); // consume closing "*/"
31 }
32 } else {
33 break;
34 }
35 }
36 }
37
38 template <typename Iterator>
39 bool expression<Iterator>::match(Iterator& it, Iterator last, char c) const
40 {
41 skip_ws(it, last);
42 if (it != last && *it == c) {
43 ++it;
44 return true;
45 }
46 return false;
47 }
48
49 template <typename Iterator>
50 bool expression<Iterator>::match_op_token(Iterator& it, Iterator last, char const* token) const
51 {
52 skip_ws(it, last);
53 Iterator cur = it;
54 char const* w = token;
55 while (*w != '\0' && cur != last && *cur == *w) {
56 ++cur;
57 ++w;
58 }
59 if (*w != '\0') {
60 return false;
61 }
62 it = cur;
63 return true;
64 }
65
66 template <typename Iterator>
67 bool expression<Iterator>::match_word(Iterator& it, Iterator last, char const* word) const
68 {
69 skip_ws(it, last);
70 Iterator cur = it;
71 char const* w = word;
72 while (*w != '\0' && cur != last && *cur == *w) {
73 ++cur;
74 ++w;
75 }
76 if (*w != '\0') {
77 return false; // did not fully match the keyword
78 }
79 if (cur != last && (std::isalnum(static_cast<unsigned char>(*cur)) || *cur == '_')) {
80 return false; // e.g. "trueish" is not the keyword "true"
81 }
82 it = cur;
83 return true;
84 }
85
86 template <typename Iterator>
87 bool expression<Iterator>::try_identifier_token(Iterator& it, Iterator last, std::string& word) const
88 {
89 skip_ws(it, last);
90 Iterator save = it;
91 if (it == last || !(std::isalpha(static_cast<unsigned char>(*it)) || *it == '_')) {
92 return false;
93 }
94 Iterator cur = it;
95 ++cur;
96 while (cur != last && (std::isalnum(static_cast<unsigned char>(*cur)) || *cur == '_')) {
97 ++cur;
98 }
99 std::string token(save, cur);
100 if (token == "true" || token == "false") {
101 return false; // keywords cannot be used as identifiers
102 }
103 it = cur;
104 word = std::move(token);
105 return true;
106 }
107
108 template <typename Iterator>
110 {
111 // Optimization over the original Spirit grammar: an expression with
112 // no trailing operations carries no extra information, so we avoid
113 // the heap allocation of wrapping it and reuse its operand directly
114 // (evaluating the wrapped form would just forward to `first`).
115 if (e.rest.empty()) {
116 return std::move(e.first);
117 }
118 return ast::operand(std::move(e));
119 }
120
122 // Binary, left-associative precedence level:
123 // level = next (op next)*
125 template <typename Iterator>
126 template <typename NextLevel, typename OpMatcher>
128 Iterator& it, Iterator last, ast::expression& out,
129 NextLevel next, OpMatcher match_op)
130 {
131 Iterator save = it;
132
133 ast::expression firstExpr;
134 if (!next(it, last, firstExpr)) {
135 it = save;
136 return false;
137 }
138
139 out.first = to_operand(std::move(firstExpr));
140 out.rest.clear();
141
142 while (true) {
143 Iterator opPos = it;
144 std::optional<ast::optoken> op = match_op(it, last);
145 if (!op) {
146 it = opPos;
147 break;
148 }
149
150 ast::expression rhsExpr;
151 if (!next(it, last, rhsExpr)) {
152 error_handler_(std::string("Error! "), std::string("Expecting operand"), it);
153 return false;
154 }
155
156 ast::operation operation{
157 *op,
158 to_operand(std::move(rhsExpr))
159 };
160 out.rest.push_back(std::move(operation));
161 }
162
163 return true;
164 }
165
166 template <typename Iterator>
167 bool expression<Iterator>::parse_logical_or(Iterator& it, Iterator last, ast::expression& out)
168 {
169 return parse_level(it, last, out,
170 [this](Iterator& i, Iterator l, ast::expression& o) { return parse_logical_and(i, l, o); },
171 [this](Iterator& i, Iterator l) -> std::optional<ast::optoken> {
172 if (match_op_token(i, l, "||")) return ast::op_or;
173 return std::nullopt;
174 });
175 }
176
177 template <typename Iterator>
178 bool expression<Iterator>::parse_logical_and(Iterator& it, Iterator last, ast::expression& out)
179 {
180 return parse_level(it, last, out,
181 [this](Iterator& i, Iterator l, ast::expression& o) { return parse_equality(i, l, o); },
182 [this](Iterator& i, Iterator l) -> std::optional<ast::optoken> {
183 if (match_op_token(i, l, "&&")) return ast::op_and;
184 return std::nullopt;
185 });
186 }
187
188 template <typename Iterator>
189 bool expression<Iterator>::parse_equality(Iterator& it, Iterator last, ast::expression& out)
190 {
191 return parse_level(it, last, out,
192 [this](Iterator& i, Iterator l, ast::expression& o) { return parse_relational(i, l, o); },
193 [this](Iterator& i, Iterator l) -> std::optional<ast::optoken> {
194 if (match_op_token(i, l, "==")) return ast::op_equal;
195 if (match_op_token(i, l, "!=")) return ast::op_not_equal;
196 return std::nullopt;
197 });
198 }
199
200 template <typename Iterator>
201 bool expression<Iterator>::parse_relational(Iterator& it, Iterator last, ast::expression& out)
202 {
203 return parse_level(it, last, out,
204 [this](Iterator& i, Iterator l, ast::expression& o) { return parse_additive(i, l, o); },
205 [this](Iterator& i, Iterator l) -> std::optional<ast::optoken> {
206 if (match_op_token(i, l, "<=")) return ast::op_less_equal;
207 if (match_op_token(i, l, ">=")) return ast::op_greater_equal;
208 if (match_op_token(i, l, "<")) return ast::op_less;
209 if (match_op_token(i, l, ">")) return ast::op_greater;
210 return std::nullopt;
211 });
212 }
213
214 template <typename Iterator>
215 bool expression<Iterator>::parse_additive(Iterator& it, Iterator last, ast::expression& out)
216 {
217 return parse_level(it, last, out,
218 [this](Iterator& i, Iterator l, ast::expression& o) { return parse_multiplicative(i, l, o); },
219 [this](Iterator& i, Iterator l) -> std::optional<ast::optoken> {
220 if (match_op_token(i, l, "+")) return ast::op_plus;
221 if (match_op_token(i, l, "-")) return ast::op_minus;
222 return std::nullopt;
223 });
224 }
225
226 template <typename Iterator>
227 bool expression<Iterator>::parse_multiplicative(Iterator& it, Iterator last, ast::expression& out)
228 {
229 return parse_level(it, last, out,
230 [this](Iterator& i, Iterator l, ast::expression& o) -> bool {
231 ast::operand operand;
232 if (!parse_unary(i, l, operand)) return false;
233 o.first = std::move(operand);
234 o.rest.clear();
235 return true;
236 },
237 [this](Iterator& i, Iterator l) -> std::optional<ast::optoken> {
238 if (match_op_token(i, l, "*")) return ast::op_times;
239 if (match_op_token(i, l, "/")) return ast::op_divide;
240 return std::nullopt;
241 });
242 }
243
244 template <typename Iterator>
245 bool expression<Iterator>::parse_unary(Iterator& it, Iterator last, ast::operand& out)
246 {
247 Iterator save = it;
248 ast::optoken op;
249 bool isUnary = true;
250 if (match(it, last, '+')) {
251 op = ast::op_positive;
252 } else if (match(it, last, '-')) {
253 op = ast::op_negative;
254 } else if (match(it, last, '!')) {
255 op = ast::op_not;
256 } else {
257 isUnary = false;
258 }
259
260 if (!isUnary) {
261 return parse_primary(it, last, out);
262 }
263
264 ast::operand sub;
265 if (!parse_unary(it, last, sub)) {
266 it = save;
267 return false;
268 }
269
270 ast::unary u;
271 u.operator_ = op;
272 u.operand_ = std::move(sub);
273 out = std::move(u);
274 return true;
275 }
276
277 template <typename Iterator>
278 bool expression<Iterator>::parse_constant(Iterator& it, Iterator last, ast::operand& out)
279 {
280 skip_ws(it, last);
281 Iterator save = it;
282 Iterator cur = it;
283
284 bool hasDigits = false;
285 while (cur != last && std::isdigit(static_cast<unsigned char>(*cur))) {
286 ++cur;
287 hasDigits = true;
288 }
289 if (cur != last && *cur == '.') {
290 Iterator afterDot = cur;
291 ++afterDot;
292 if (afterDot != last && std::isdigit(static_cast<unsigned char>(*afterDot))) {
293 cur = afterDot;
294 while (cur != last && std::isdigit(static_cast<unsigned char>(*cur))) {
295 ++cur;
296 hasDigits = true;
297 }
298 } else if (hasDigits) {
299 cur = afterDot; // trailing dot after digits, e.g. "3."
300 }
301 }
302 if (!hasDigits) {
303 return false;
304 }
305 if (cur != last && (*cur == 'e' || *cur == 'E')) {
306 Iterator expIt = cur;
307 ++expIt;
308 if (expIt != last && (*expIt == '+' || *expIt == '-')) {
309 ++expIt;
310 }
311 if (expIt != last && std::isdigit(static_cast<unsigned char>(*expIt))) {
312 while (expIt != last && std::isdigit(static_cast<unsigned char>(*expIt))) {
313 ++expIt;
314 }
315 cur = expIt;
316 }
317 }
318
319 std::string token(save, cur);
320 char* end = nullptr;
321 double value = std::strtod(token.c_str(), &end);
322 if (end != token.c_str() + token.size()) {
323 return false;
324 }
325
326 it = cur;
327 out = value;
328 return true;
329 }
330
331 template <typename Iterator>
332 bool expression<Iterator>::parse_primary(Iterator& it, Iterator last, ast::operand& out)
333 {
334 if (parse_constant(it, last, out)) {
335 return true;
336 }
337
338 Iterator save = it;
339 std::string word;
340 if (try_identifier_token(it, last, word)) {
341 Iterator afterWord = it;
342 if (match(it, last, '(')) {
345 std::list<ast::function_call_argument> args;
346 if (!parse_argument_list(it, last, args)) {
347 return false;
348 }
349 fc.args = std::move(args);
350 if (!match(it, last, ')')) {
351 error_handler_(std::string("Error! "), std::string("Expecting \")\""), it);
352 return false;
353 }
354 out = std::move(fc);
355 annotation_(out, save);
356 return true;
357 }
358
359 it = afterWord;
360 ast::identifier id(word);
361 out = id;
362 annotation_(out, save);
363 return true;
364 }
365 it = save;
366
367 if (match_word(it, last, "true")) {
368 out = true;
369 annotation_(out, save);
370 return true;
371 }
372 if (match_word(it, last, "false")) {
373 out = false;
374 annotation_(out, save);
375 return true;
376 }
377
378 if (match(it, last, '(')) {
380 if (!parse_expr(it, last, e)) {
381 return false;
382 }
383 if (!match(it, last, ')')) {
384 error_handler_(std::string("Error! "), std::string("Expecting \")\""), it);
385 return false;
386 }
387 out = to_operand(std::move(e));
388 annotation_(out, save);
389 return true;
390 }
391
392 it = save;
393 return false;
394 }
395
396 template <typename Iterator>
398 {
399 Iterator save = it;
400
402 if (parse_expr(it, last, e)) {
403 out = std::move(e);
404 return true;
405 }
406 it = save;
407
408 std::string s;
409 if (parse_quoted_string(it, last, s)) {
410 out = ast::quoted_string(s);
411 return true;
412 }
413 it = save;
414
415 return false;
416 }
417
418 template <typename Iterator>
419 bool expression<Iterator>::parse_argument_list(Iterator& it, Iterator last, std::list<ast::function_call_argument>& out)
420 {
421 out.clear();
422
424 if (!parse_call_argument(it, last, firstArg)) {
425 return true; // the argument list is optional
426 }
427 out.push_back(std::move(firstArg));
428
429 while (true) {
430 Iterator commaPos = it;
431 if (!match(it, last, ',')) {
432 it = commaPos;
433 break;
434 }
435
437 if (!parse_call_argument(it, last, arg)) {
438 error_handler_(std::string("Error! "), std::string("Expecting argument"), it);
439 return false;
440 }
441 out.push_back(std::move(arg));
442 }
443
444 return true;
445 }
446
447 template <typename Iterator>
448 bool expression<Iterator>::parse_quoted_string(Iterator& it, Iterator last, std::string& out)
449 {
450 skip_ws(it, last);
451 if (it == last || (*it != '\'' && *it != '"')) {
452 return false;
453 }
454
455 char quote = *it;
456 Iterator cur = it;
457 ++cur;
458 std::string value;
459 while (cur != last && *cur != quote) {
460 value += *cur;
461 ++cur;
462 }
463 if (cur == last) {
464 return false; // unterminated string
465 }
466 ++cur; // consume closing quote
467 it = cur;
468 out = std::move(value);
469 return true;
470 }
471
472 template <typename Iterator>
473 bool expression<Iterator>::parse_expr(Iterator& it, Iterator last, ast::expression& out)
474 {
475 return parse_logical_or(it, last, out);
476 }
477
478 template <typename Iterator>
479 bool expression<Iterator>::parse(Iterator& first, Iterator last, ast::expression& attr)
480 {
481 Iterator it = first;
482 if (!parse_expr(it, last, attr)) {
483 error_handler_(std::string("Error! "), std::string("Expecting expression"), it);
484 return false;
485 }
486 skip_ws(it, last);
487 first = it;
488 return true;
489 }
490}}
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
arg(a))
@ op_not_equal
Definition ast.hpp:142
@ op_less_equal
Definition ast.hpp:144
@ op_greater
Definition ast.hpp:145
@ op_greater_equal
Definition ast.hpp:146
@ op_positive
Definition ast.hpp:138
@ op_negative
Definition ast.hpp:139
@ op_divide
Definition ast.hpp:137
std::variant< expression, quoted_string > function_call_argument
Definition ast.hpp:130
std::variant< nil, bool, unsigned int, double, identifier, recursive_wrapper< unary >, recursive_wrapper< function_call >, recursive_wrapper< expression > > operand
Definition ast.hpp:124
operand operand_
Definition ast.hpp:154
optoken operator_
Definition ast.hpp:153
std::list< function_call_argument > args
Definition ast.hpp:166
identifier function_name
Definition ast.hpp:165
std::list< operation > rest
Definition ast.hpp:172
bool parse_logical_and(Iterator &it, Iterator last, ast::expression &out)
bool parse_primary(Iterator &it, Iterator last, ast::operand &out)
bool parse_multiplicative(Iterator &it, Iterator last, ast::expression &out)
bool parse_argument_list(Iterator &it, Iterator last, std::list< ast::function_call_argument > &out)
bool parse_quoted_string(Iterator &it, Iterator last, std::string &out)
bool parse_call_argument(Iterator &it, Iterator last, ast::function_call_argument &out)
expression(error_handler< Iterator > &error_handler)
bool match_word(Iterator &it, Iterator last, char const *word) const
bool parse_constant(Iterator &it, Iterator last, ast::operand &out)
bool parse_relational(Iterator &it, Iterator last, ast::expression &out)
bool parse_level(Iterator &it, Iterator last, ast::expression &out, NextLevel next, OpMatcher match_op)
bool match_op_token(Iterator &it, Iterator last, char const *token) const
bool parse(Iterator &first, Iterator last, ast::expression &attr)
bool parse_expr(Iterator &it, Iterator last, ast::expression &out)
static ast::operand to_operand(ast::expression &&e)
void skip_ws(Iterator &it, Iterator last) const
bool parse_logical_or(Iterator &it, Iterator last, ast::expression &out)
bool parse_unary(Iterator &it, Iterator last, ast::operand &out)
bool parse_equality(Iterator &it, Iterator last, ast::expression &out)
bool try_identifier_token(Iterator &it, Iterator last, std::string &word) const
bool match(Iterator &it, Iterator last, char c) const
bool parse_additive(Iterator &it, Iterator last, ast::expression &out)