1 /*
2 Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY.
11
12 See the COPYING file for more details.
13 */
14
15 #include "formula/formula.hpp"
16
17 #include "formula/callable.hpp"
18 #include "formula/function.hpp"
19 #include "formula/string_utils.hpp"
20 #include "random.hpp"
21 #include "serialization/string_utils.hpp"
22 #include "log.hpp"
23
24 #include <cassert>
25 #include <set>
26 #include <sstream>
27
28 // This is here only for the below initialization code.
29 // If other logging is required in this file, it should use a different logdomain
30 // (probably "scripting/formula" or something)
31 static lg::log_domain log_engine("engine");
32 #define ERR_NG LOG_STREAM(err, log_engine)
33
34 namespace utils {
35 namespace detail {
36 std::string evaluate_formula_impl(const std::string&);
37
evaluate_formula_impl(const std::string & formula)38 std::string evaluate_formula_impl(const std::string& formula) {
39 try {
40 const wfl::formula form(formula);
41 return form.evaluate().string_cast();
42 } catch(const wfl::formula_error& e) {
43 ERR_NG << "Formula in WML string cannot be evaluated due to "
44 << e.type << "\n\t--> \"";
45 return "";
46 }
47 }
48
49 struct formula_initer {
formula_initerutils::detail::formula_initer50 formula_initer() {
51 evaluate_formula = &evaluate_formula_impl;
52 }
53 } init;
54 }
55 }
56
57 namespace wfl
58 {
59 using expr_table = std::map<std::string, expression_ptr>;
60 using expr_table_evaluated = std::map<std::string, variant>;
61 using expr_table_ptr = std::shared_ptr<expr_table>;
62
63 // Function used when creating error reports.
64 // Parses all tokens passed to parse_expression, thus there are no EOL or whitespaces
tokens_to_string(const tk::token * i1,const tk::token * i2)65 static std::string tokens_to_string(const tk::token* i1, const tk::token* i2)
66 {
67 std::ostringstream expr;
68 while(i1 != i2) {
69 expr << std::string(i1->begin, i1->end) << " ";
70 ++i1;
71 }
72
73 return expr.str();
74 }
75
76 class null_expression : public formula_expression
77 {
78 public:
null_expression()79 null_expression() {}
80
str() const81 std::string str() const
82 {
83 return "";
84 }
85
86 private:
execute(const formula_callable &,formula_debugger *) const87 variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
88 {
89 return variant();
90 }
91 };
92
93 // Implemented further down
94 expression_ptr parse_expression(const tk::token* i1, const tk::token* i2, function_symbol_table* symbols);
95
96
97 const char* const formula::id_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
98
formula(const std::string & text,function_symbol_table * symbols)99 formula::formula(const std::string& text, function_symbol_table* symbols)
100 : expr_()
101 , str_(text)
102 , managed_symbols_(symbols ? nullptr : new function_symbol_table)
103 , symbols_(symbols ? symbols : managed_symbols_.get())
104 {
105 std::vector<tk::token> tokens;
106 std::string::const_iterator i1 = text.begin(), i2 = text.end();
107
108 // Set true when 'fai' keyword is found
109 bool fai_keyword = false;
110
111 // Set true when 'wfl' keyword is found
112 bool wfl_keyword = false;
113
114 // Used to locally keep the track of which file we parse actually and in which line we are
115 std::vector<std::pair<std::string, int>> files;
116
117 // Used as a source of strings - we point to these strings from tokens
118 std::set<std::string> filenames;
119
120 files.emplace_back("formula", 1);
121 filenames.insert("formula");
122
123 std::set<std::string>::iterator filenames_it = filenames.begin();
124
125 while(i1 != i2) {
126 try {
127 tokens.push_back(tk::get_token(i1,i2));
128
129 tk::TOKEN_TYPE current_type = tokens.back().type;
130
131 if(current_type == tk::TOKEN_WHITESPACE) {
132 tokens.pop_back();
133 } else if(current_type == tk::TOKEN_COMMENT) {
134 // Since we can have multiline comments, let's see how many EOL are within it
135 int counter = 0;
136
137 std::string comment = std::string(tokens.back().begin, tokens.back().end);
138 for(const auto& str_it : comment) {
139 if(str_it == '\n') {
140 counter++;
141 }
142 }
143
144 files.back().second += counter;
145 tokens.pop_back();
146 } else if(current_type == tk::TOKEN_EOL) {
147 files.back().second++;
148 tokens.pop_back();
149 } else if((current_type == tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) == "fai")) {
150 fai_keyword = true;
151 tokens.pop_back();
152 } else if((current_type == tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) == "wfl")) {
153 wfl_keyword = true;
154 tokens.pop_back();
155 } else if((current_type == tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) == "faiend")) {
156 if(files.size() > 1) {
157 files.pop_back();
158 filenames_it = filenames.find(files.back().first);
159
160 tokens.pop_back();
161 } else {
162 throw formula_error("Unexpected 'faiend' found", "", "", 0);
163 }
164 } else if((current_type == tk::TOKEN_KEYWORD) && (std::string(tokens.back().begin, tokens.back().end) == "wflend")) {
165 if(files.size() > 1) {
166 files.pop_back();
167 filenames_it = filenames.find(files.back().first);
168
169 tokens.pop_back();
170 } else {
171 throw formula_error("Unexpected 'wflend' found", "", "", 0);
172 }
173 } else if(fai_keyword || wfl_keyword) {
174 if(current_type == tk::TOKEN_STRING_LITERAL) {
175 std::string str = std::string(tokens.back().begin, tokens.back().end);
176 files.emplace_back(str , 1);
177
178 std::set<std::string>::iterator pos;
179 bool success;
180
181 std::tie(pos, success) = filenames.insert(str);
182
183 if(success) {
184 filenames_it = pos;
185 } else {
186 if(fai_keyword) {
187 throw formula_error("Faifile already included", "fai" + str, "", 0);
188 } else {
189 throw formula_error("Wflfile already included", "wfl" + str, "", 0);
190 }
191 }
192
193 tokens.pop_back();
194 fai_keyword = false;
195 wfl_keyword = false;
196 } else {
197 if(fai_keyword) {
198 throw formula_error("Expected string after the 'fai'", "fai", "", 0);
199 } else {
200 throw formula_error("Expected string after the 'wfl'", "wfl", "", 0);
201 }
202 }
203 } else {
204 // In every token not specified above, store line number and name of file it came from
205 tokens.back().filename = &(*filenames_it);
206 tokens.back().line_number = files.back().second;
207 }
208 } catch(const tk::token_error& e) {
209 // When we catch token error, we should write whole line in which error occurred,
210 // so we merge info from token and everything we had in the line so far
211 std::string str = "";
212 if(!tokens.empty()) {
213 tk::token* tok_it = &tokens[0] + tokens.size()-1;
214 while(( tok_it != &tokens[0] ) && (tok_it->line_number == tokens.back().line_number)) {
215 --tok_it;
216 }
217
218 if(tok_it != &tokens[0] && tok_it != &tokens[0] + tokens.size() -1) {
219 ++tok_it;
220 }
221
222 str = tokens_to_string(tok_it, &tokens[0] + tokens.size());
223 }
224
225 throw formula_error(e.description_, str + e.formula_, *filenames_it, files.back().second);
226 }
227 }
228
229 if(files.size() > 1) {
230 throw formula_error("Missing 'wflend', make sure each .wfl file ends with it", "", "", 0);
231 }
232
233 if(!tokens.empty()) {
234 expr_ = parse_expression(&tokens[0], &tokens[0] + tokens.size(), symbols_);
235 } else {
236 expr_ = std::make_shared<null_expression>();
237 }
238 }
239
formula(const tk::token * i1,const tk::token * i2,function_symbol_table * symbols)240 formula::formula(const tk::token* i1, const tk::token* i2, function_symbol_table* symbols)
241 : expr_()
242 , str_()
243 , managed_symbols_(symbols ? nullptr : new function_symbol_table)
244 , symbols_(symbols ? symbols : managed_symbols_.get())
245 {
246 if(i1 != i2) {
247 expr_ = parse_expression(i1, i2, symbols);
248 } else {
249 expr_ = std::make_shared<null_expression>();
250 }
251 }
252
create_optional_formula(const std::string & str,function_symbol_table * symbols)253 formula_ptr formula::create_optional_formula(const std::string& str, function_symbol_table* symbols)
254 {
255 if(str.empty()) {
256 return formula_ptr();
257 }
258
259 return formula_ptr(new formula(str, symbols));
260 }
261
execute(const formula_callable & variables,formula_debugger * fdb) const262 variant formula::execute(const formula_callable& variables, formula_debugger*fdb) const
263 {
264 try {
265 return expr_->evaluate(variables, fdb);
266 } catch(const type_error& e) {
267 std::cerr << "formula type error: " << e.message << "\n";
268 return variant();
269 }
270 }
271
execute(formula_debugger * fdb) const272 variant formula::execute(formula_debugger*fdb) const
273 {
274 static map_formula_callable null_callable;
275 return execute(null_callable,fdb);
276 }
277
278
formula_error(const std::string & type,const std::string & formula,const std::string & file,int line)279 formula_error::formula_error(const std::string& type, const std::string& formula,
280 const std::string& file, int line)
281 : error()
282 , type(type)
283 , formula(formula)
284 , filename(file)
285 , line(line)
286 {
287 std::stringstream ss;
288 ss << "Formula error in " << filename << ":" << line
289 << "\nIn formula " << formula
290 << "\nError: " << type;
291 message = ss.str();
292 }
293
294
295 /**
296 * Classes that encapsulate and handle the various formula functions.
297 */
298 class function_list_expression : public formula_expression
299 {
300 public:
function_list_expression(function_symbol_table * symbols)301 explicit function_list_expression(function_symbol_table* symbols)
302 : symbols_(symbols)
303 {}
304
str() const305 virtual std::string str() const
306 {
307 return "{function_list_expression()}";
308 }
309
310 private:
execute(const formula_callable &,formula_debugger *) const311 variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
312 {
313 std::vector<variant> res;
314 for(const std::string& fcn_name : symbols_->get_function_names()) {
315 res.emplace_back(fcn_name);
316 }
317
318 return variant(res);
319 }
320
321 function_symbol_table* symbols_;
322 };
323
324 class list_expression : public formula_expression
325 {
326 public:
list_expression(const std::vector<expression_ptr> & items)327 explicit list_expression(const std::vector<expression_ptr>& items)
328 : items_(items)
329 {}
330
331 private:
execute(const formula_callable & variables,formula_debugger * fdb) const332 variant execute(const formula_callable& variables, formula_debugger*fdb) const
333 {
334 std::vector<variant> res;
335 res.reserve(items_.size());
336 for(const auto& i : items_) {
337 res.push_back(i->evaluate(variables, add_debug_info(fdb, 0, "[list element]")));
338 }
339
340 return variant(res);
341 }
342
343 std::vector<expression_ptr> items_;
344
str() const345 std::string str() const
346 {
347 std::stringstream s;
348 s << '[';
349 bool first_item = true;
350 for(expression_ptr a : items_) {
351 if(!first_item) {
352 s << ',';
353 } else {
354 first_item = false;
355 }
356 s << a->str();
357 }
358 s << ']';
359 return s.str();
360 }
361 };
362
363 class map_expression : public formula_expression
364 {
365 public:
map_expression(const std::vector<expression_ptr> & items)366 explicit map_expression(const std::vector<expression_ptr>& items)
367 : items_(items)
368 {}
369
str() const370 virtual std::string str() const
371 {
372 std::stringstream s;
373 s << " [";
374 for(std::vector<expression_ptr>::const_iterator i = items_.begin(); (i != items_.end()) && (i + 1 != items_.end()) ; i += 2) {
375 if(i != items_.begin()) {
376 s << ", ";
377 }
378 s << (*i)->str();
379 s << " -> ";
380 s << (*(i+1))->str();
381 }
382 if(items_.empty()) {
383 s << "->";
384 }
385 s << " ]";
386 return s.str();
387 }
388
389 private:
execute(const formula_callable & variables,formula_debugger * fdb) const390 variant execute(const formula_callable& variables, formula_debugger*fdb) const
391 {
392 std::map<variant,variant> res;
393 for(std::vector<expression_ptr>::const_iterator i = items_.begin(); (i != items_.end()) && (i + 1 != items_.end()) ; i += 2) {
394 variant key = (*i)->evaluate(variables, add_debug_info(fdb, 0, "key ->"));
395 variant value = (*(i+1))->evaluate(variables, add_debug_info(fdb, 1, "-> value"));
396 res[key] = value;
397 }
398
399 return variant(res);
400 }
401
402 std::vector<expression_ptr> items_;
403 };
404
405 class unary_operator_expression : public formula_expression
406 {
407 public:
unary_operator_expression(const std::string & op,expression_ptr arg)408 unary_operator_expression(const std::string& op, expression_ptr arg)
409 : op_(),op_str_(op)
410 , operand_(arg)
411 {
412 if(op == "not") {
413 op_ = NOT;
414 } else if(op == "-") {
415 op_ = SUB;
416 } else {
417 throw formula_error("Illegal unary operator: '" + op + "'" , "", "", 0);
418 }
419 }
420
str() const421 virtual std::string str() const
422 {
423 std::stringstream s;
424 s << op_str_ << '('<< operand_->str() << ')';
425 return s.str();
426 }
427
428 private:
execute(const formula_callable & variables,formula_debugger * fdb) const429 variant execute(const formula_callable& variables, formula_debugger*fdb) const
430 {
431 const variant res = operand_->evaluate(variables, add_debug_info(fdb, 0, op_str_ + " unary"));
432 switch(op_) {
433 case NOT:
434 return res.as_bool() ? variant(0) : variant(1);
435 case SUB:
436 default:
437 return -res;
438 }
439 }
440
441 enum OP { NOT, SUB };
442 OP op_;
443 std::string op_str_;
444 expression_ptr operand_;
445 };
446
447 class string_callable : public formula_callable
448 {
449 public:
string_callable(const variant & string)450 explicit string_callable(const variant& string) : string_(string) {}
451
get_inputs(formula_input_vector & inputs) const452 void get_inputs(formula_input_vector& inputs) const
453 {
454 add_input(inputs, "size");
455 add_input(inputs, "empty");
456 add_input(inputs, "char");
457 add_input(inputs, "word");
458 add_input(inputs, "item");
459 }
460
get_value(const std::string & key) const461 variant get_value(const std::string& key) const
462 {
463 if(key == "size") {
464 return variant(string_.as_string().length());
465 } else if(key == "empty") {
466 return variant(string_.as_string().empty());
467 } else if(key == "char" || key == "chars") {
468 std::vector<variant> chars;
469 for(char c : string_.as_string()) {
470 chars.emplace_back(std::string(1, c));
471 }
472
473 return variant(chars);
474 } else if(key == "word" || key == "words") {
475 std::vector<variant> words;
476 const std::string& str = string_.as_string();
477 size_t next_space = 0;
478 do {
479 size_t last_space = next_space;
480 next_space = str.find_first_of(" \t", next_space);
481 words.emplace_back(str.substr(last_space, next_space - last_space));
482 next_space = str.find_first_not_of(" \t", next_space);
483 } while(next_space != std::string::npos);
484
485 return variant(words);
486 } else if(key == "item" || key == "items") {
487 std::vector<std::string> split = utils::parenthetical_split(string_.as_string(), ',');
488 std::vector<variant> items;
489 items.reserve(split.size());
490 for(const std::string s : split) {
491 items.emplace_back(s);
492 }
493
494 return variant(items);
495 }
496
497 return variant();
498 }
499
500 private:
501 variant string_;
502 };
503
504 class list_callable : public formula_callable
505 {
506 public:
list_callable(const variant & list)507 explicit list_callable(const variant& list) : list_(list) {}
508
get_inputs(formula_input_vector & inputs) const509 void get_inputs(formula_input_vector& inputs) const
510 {
511 add_input(inputs, "size", FORMULA_READ_WRITE);
512 add_input(inputs, "empty", FORMULA_READ_WRITE);
513 add_input(inputs, "first", FORMULA_READ_WRITE);
514 add_input(inputs, "last", FORMULA_READ_WRITE);
515 }
516
get_value(const std::string & key) const517 variant get_value(const std::string& key) const
518 {
519 if(key == "size") {
520 return variant(list_.num_elements());
521 } else if(key == "empty") {
522 return variant(list_.num_elements() == 0);
523 } else if(key == "first") {
524 if(list_.num_elements() > 0) {
525 return list_[0];
526 }
527
528 return variant();
529 } else if(key == "last") {
530 if(list_.num_elements() > 0) {
531 return list_[list_.num_elements()-1];
532 }
533
534 return variant();
535 }
536
537 return variant();
538 }
539
540 private:
541 variant list_;
542 };
543
544 class map_callable : public formula_callable
545 {
546 public:
map_callable(const variant & map)547 explicit map_callable(const variant& map) : map_(map) {}
548
get_inputs(formula_input_vector & inputs) const549 void get_inputs(formula_input_vector& inputs) const
550 {
551 add_input(inputs, "size", FORMULA_READ_WRITE);
552 add_input(inputs, "empty", FORMULA_READ_WRITE);
553
554 for(const auto& v : map_) {
555 // variant_iterator does not implement operator->,
556 // and to do so is notrivial since it returns temporaries for maps.
557 const variant& key_variant = v.get_member("key");
558 if(!key_variant.is_string()) {
559 continue;
560 }
561
562 std::string key = key_variant.as_string();
563 bool valid = true;
564 for(char c : key) {
565 if(!isalpha(c) && c != '_') {
566 valid = false;
567 break;
568 }
569 }
570
571 if(valid) {
572 add_input(inputs, key);
573 }
574 }
575 }
576
get_value(const std::string & key) const577 variant get_value(const std::string& key) const
578 {
579 const variant key_variant(key);
580 if(map_.as_map().find(key_variant) != map_.as_map().end()) {
581 return map_[key_variant];
582 } else if(key == "size") {
583 return variant(map_.num_elements());
584 } else if(key == "empty") {
585 return variant(map_.num_elements() == 0);
586 }
587
588 return variant();
589 }
590
591 private:
592 variant map_;
593 };
594
595 class dot_callable : public formula_callable
596 {
597 public:
dot_callable(const formula_callable & global,const formula_callable & local)598 dot_callable(const formula_callable &global, const formula_callable& local)
599 : global_(global), local_(local)
600 {}
601
602 private:
603 const formula_callable& global_, &local_;
604
get_inputs(formula_input_vector & inputs) const605 void get_inputs(formula_input_vector& inputs) const
606 {
607 return local_.get_inputs(inputs);
608 }
609
get_value(const std::string & key) const610 variant get_value(const std::string& key) const
611 {
612 variant v = local_.query_value(key);
613
614 if( v == variant() )
615 return global_.query_value(key);
616 else
617 return v;
618 }
619 };
620
621 class dot_expression : public formula_expression
622 {
623 public:
dot_expression(expression_ptr left,expression_ptr right)624 dot_expression(expression_ptr left, expression_ptr right)
625 : left_(left), right_(right)
626 {}
627
str() const628 std::string str() const
629 {
630 std::stringstream s;
631 s << left_->str() << "." << right_->str();
632 return s.str();
633 }
634
635 private:
execute(const formula_callable & variables,formula_debugger * fdb) const636 variant execute(const formula_callable& variables, formula_debugger*fdb) const
637 {
638 const variant left = left_->evaluate(variables, add_debug_info(fdb,0,"left ."));
639 if(!left.is_callable()) {
640 if(left.is_list()) {
641 list_callable list_call(left);
642 dot_callable callable(variables, list_call);
643 return right_->evaluate(callable,fdb);
644 }
645
646 if(left.is_map()) {
647 map_callable map_call(left);
648 dot_callable callable(variables, map_call);
649 return right_->evaluate(callable,fdb);
650 }
651
652 if(left.is_string()) {
653 string_callable string_call(left);
654 dot_callable callable(variables, string_call);
655 return right_->evaluate(callable,fdb);
656 }
657
658 return left;
659 }
660
661 dot_callable callable(variables, *left.as_callable());
662 return right_->evaluate(callable, add_debug_info(fdb,1,". right"));
663 }
664
665 expression_ptr left_, right_;
666 };
667
668 class square_bracket_expression : public formula_expression
669 {
670 public:
square_bracket_expression(expression_ptr left,expression_ptr key)671 square_bracket_expression(expression_ptr left, expression_ptr key)
672 : left_(left), key_(key)
673 {}
674
str() const675 std::string str() const
676 {
677 std::stringstream s;
678 s << left_->str() << '[' << key_->str() << ']';
679 return s.str();
680 }
681
682 private:
execute(const formula_callable & variables,formula_debugger * fdb) const683 variant execute(const formula_callable& variables, formula_debugger*fdb) const
684 {
685 const variant left = left_->evaluate(variables, add_debug_info(fdb,0,"base[]"));
686 const variant key = key_->evaluate(variables, add_debug_info(fdb,1,"[index]"));
687 if(left.is_list() || left.is_map()) {
688 return left[key];
689 }
690
691 return variant();
692 }
693
694 expression_ptr left_, key_;
695 };
696
697 class operator_expression : public formula_expression
698 {
699 public:
operator_expression(const std::string & op,expression_ptr left,expression_ptr right)700 operator_expression(const std::string& op, expression_ptr left, expression_ptr right)
701 : op_(OP(op[0])), op_str_(op), left_(left), right_(right)
702 {
703 if(op == ">=") {
704 op_ = GTE;
705 } else if(op == "<=") {
706 op_ = LTE;
707 } else if(op == "!=") {
708 op_ = NEQ;
709 } else if(op == "and") {
710 op_ = AND;
711 } else if(op == "or") {
712 op_ = OR;
713 } else if(op == ".+") {
714 op_ = ADDL;
715 } else if(op == ".-") {
716 op_ = SUBL;
717 } else if(op == ".*") {
718 op_ = MULL;
719 } else if(op == "./") {
720 op_ = DIVL;
721 } else if(op == "..") {
722 op_ = OP_CAT;
723 } else if(op == "in") {
724 op_ = OP_IN;
725 }
726 }
727
str() const728 std::string str() const
729 {
730 std::stringstream s;
731 s << '(' << left_->str() << op_str_ << right_->str() << ')';
732 return s.str();
733 }
734
735 private:
execute(const formula_callable & variables,formula_debugger * fdb) const736 variant execute(const formula_callable& variables, formula_debugger*fdb) const
737 {
738 const variant left = left_->evaluate(variables, add_debug_info(fdb, 0, "left " + op_str_));
739 const variant right = right_->evaluate(variables, add_debug_info(fdb, 1, op_str_ + " right"));
740
741 switch(op_) {
742 case AND:
743 return left.as_bool() == false ? left : right;
744 case OR:
745 return left.as_bool() ? left : right;
746 case ADD:
747 return left + right;
748 case SUB:
749 return left - right;
750 case MUL:
751 return left * right;
752 case DIV:
753 return left / right;
754 case POW:
755 return left ^ right;
756 case ADDL:
757 return left.list_elements_add(right);
758 case SUBL:
759 return left.list_elements_sub(right);
760 case MULL:
761 return left.list_elements_mul(right);
762 case DIVL:
763 return left.list_elements_div(right);
764 case OP_IN:
765 return variant(right.contains(left));
766 case OP_CAT:
767 return left.concatenate(right);
768 case EQ:
769 return left == right ? variant(1) : variant(0);
770 case NEQ:
771 return left != right ? variant(1) : variant(0);
772 case LTE:
773 return left <= right ? variant(1) : variant(0);
774 case GTE:
775 return left >= right ? variant(1) : variant(0);
776 case LT:
777 return left < right ? variant(1) : variant(0);
778 case GT:
779 return left > right ? variant(1) : variant(0);
780 case MOD:
781 return left % right;
782 case RAN:
783 return left.build_range(right);
784 case DICE:
785 return variant(dice_roll(left.as_int(), right.as_int()));
786 default:
787 std::cerr << "ERROR: Unimplemented operator!" << std::endl;
788 return variant();
789 }
790 }
791
dice_roll(int num_rolls,int faces)792 static int dice_roll(int num_rolls, int faces)
793 {
794 int res = 0;
795 while(faces > 0 && num_rolls-- > 0) {
796 res += randomness::generator->get_random_int(1, faces);
797 }
798
799 return res;
800 }
801
802 //In some cases a IN or CAT macros are defined.
803 enum OP { AND, OR, NEQ, LTE, GTE, OP_CAT, OP_IN, GT='>', LT='<', EQ='=', RAN='~',
804 ADD='+', SUB='-', MUL='*', DIV='/', ADDL, SUBL, MULL, DIVL, DICE='d', POW='^', MOD='%' };
805
806 OP op_;
807 std::string op_str_;
808 expression_ptr left_, right_;
809 };
810
811 class where_variables: public formula_callable
812 {
813 public:
where_variables(const formula_callable & base,expr_table_ptr table,formula_debugger * fdb)814 where_variables(const formula_callable &base, expr_table_ptr table, formula_debugger* fdb)
815 : formula_callable(false)
816 , base_(base)
817 , table_(table)
818 , evaluated_table_()
819 , debugger_(fdb)
820 {
821 }
822
823 private:
824 const formula_callable& base_;
825 expr_table_ptr table_;
826 mutable expr_table_evaluated evaluated_table_;
827 formula_debugger* debugger_;
828
get_inputs(formula_input_vector & inputs) const829 void get_inputs(formula_input_vector& inputs) const
830 {
831 for(expr_table::const_iterator i = table_->begin(); i != table_->end(); ++i) {
832 add_input(inputs, i->first);
833 }
834 }
835
get_value(const std::string & key) const836 variant get_value(const std::string& key) const
837 {
838 expr_table::iterator i = table_->find(key);
839 if(i != table_->end()) {
840 expr_table_evaluated::const_iterator ev = evaluated_table_.find(key);
841 if(ev != evaluated_table_.end()) {
842 return ev->second;
843 }
844
845 variant v = i->second->evaluate(base_, add_debug_info(debugger_, 0, "where[" + key + "]"));
846 evaluated_table_[key] = v;
847 return v;
848 }
849
850 return base_.query_value(key);
851 }
852 };
853
854 class where_expression: public formula_expression
855 {
856 public:
where_expression(expression_ptr body,expr_table_ptr clauses)857 where_expression(expression_ptr body, expr_table_ptr clauses)
858 : body_(body), clauses_(clauses)
859 {}
860
str() const861 std::string str() const
862 {
863 std::stringstream s;
864 s << "{where:(";
865 s << body_->str();
866 for(const expr_table::value_type &a : *clauses_) {
867 s << ", [" << a.first << "] -> ["<< a.second->str()<<"]";
868 }
869 s << ")}";
870 return s.str();
871 }
872
873 private:
874 expression_ptr body_;
875 expr_table_ptr clauses_;
876
execute(const formula_callable & variables,formula_debugger * fdb) const877 variant execute(const formula_callable& variables,formula_debugger*fdb) const
878 {
879 where_variables wrapped_variables(variables, clauses_, fdb);
880 return body_->evaluate(wrapped_variables, add_debug_info(fdb, 0, "... where"));
881 }
882 };
883
884
885 class identifier_expression : public formula_expression
886 {
887 public:
identifier_expression(const std::string & id)888 explicit identifier_expression(const std::string& id) : id_(id) {}
889
str() const890 std::string str() const
891 {
892 return id_;
893 }
894
895 private:
execute(const formula_callable & variables,formula_debugger *) const896 variant execute(const formula_callable& variables, formula_debugger* /*fdb*/) const
897 {
898 return variables.query_value(id_);
899 }
900
901 std::string id_;
902 };
903
904 class integer_expression : public formula_expression
905 {
906 public:
integer_expression(int i)907 explicit integer_expression(int i) : i_(i) {}
908
str() const909 std::string str() const
910 {
911 std::stringstream s;
912 s << i_;
913 return s.str();
914 }
915
916 private:
execute(const formula_callable &,formula_debugger *) const917 variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
918 {
919 return variant(i_);
920 }
921
922 int i_;
923 };
924
925 class decimal_expression : public formula_expression
926 {
927 public:
decimal_expression(int i,int f)928 decimal_expression(int i, int f) : i_(i), f_(f) {}
929
str() const930 std::string str() const
931 {
932 std::stringstream s;
933 s << i_ << '.';
934 s.width(3);
935 s.fill('0');
936 s << f_;
937 return s.str();
938 }
939
940 private:
execute(const formula_callable &,formula_debugger *) const941 variant execute(const formula_callable& /*variables*/, formula_debugger* /*fdb*/) const
942 {
943 return variant(i_ * 1000 + f_, variant::DECIMAL_VARIANT );
944 }
945
946 int i_, f_;
947 };
948
949 class string_expression : public formula_expression
950 {
951 public:
string_expression(std::string str)952 explicit string_expression(std::string str)
953 : str_()
954 , subs_()
955 {
956 std::string::iterator i = str.begin();
957 while((i = std::find(i, str.end(), '[')) != str.end()) {
958 int bracket_depth = 0;
959 std::string::iterator j = i + 1;
960 while(j != str.end() && (bracket_depth > 0 || *j != ']')) {
961 if(*j == '[') {
962 bracket_depth++;
963 } else if(*j == ']' && bracket_depth > 0) {
964 bracket_depth--;
965 }
966 ++j;
967 }
968
969 if(j == str.end()) {
970 break;
971 }
972
973 const std::string formula_str(i+1, j);
974 const int pos = std::distance(str.begin(), i);
975 if(j - i == 2 && (i[1] == '(' || i[1] == '\'' || i[1] == ')')) {
976 // Bracket contained nothing but a quote or parenthesis.
977 // This means it was intended as a literal quote or square bracket.
978 i = str.erase(i);
979 if(*i == '(') {
980 *i = '[';
981 } else if(*i == ')') {
982 *i = ']';
983 }
984
985 i = str.erase(i + 1);
986 continue;
987 } else {
988 i = str.erase(i, j+1);
989 }
990
991 substitution sub;
992 sub.pos = pos;
993 try {
994 sub.calculation.reset(new formula(formula_str));
995 } catch(formula_error& e) {
996 e.filename += " - string substitution";
997 throw;
998 }
999
1000 subs_.push_back(sub);
1001 }
1002
1003 std::reverse(subs_.begin(), subs_.end());
1004
1005 str_ = variant(str);
1006 }
1007
str() const1008 std::string str() const
1009 {
1010 std::string res = str_.as_string();
1011 int j = res.size() - 1;
1012
1013 for(const auto& sub : subs_) {
1014 for(; j >= sub.pos && j >= 0; j--) {
1015 if(res[j] == '\'') {
1016 res.replace(j, 1, "[']");
1017 } else if(res[j] == '[') {
1018 res.replace(j, 1, "[(]");
1019 } else if(res[j] == ']') {
1020 res.replace(j, 1, "[)]");
1021 }
1022 }
1023
1024 const std::string str = "[" + sub.calculation->str() + "]";
1025 res.insert(sub.pos, str);
1026 }
1027
1028 for(; j >= 0; j--) {
1029 if(res[j] == '\'') {
1030 res.replace(j, 1, "[']");
1031 } else if(res[j] == '[') {
1032 res.replace(j, 1, "[(]");
1033 } else if(res[j] == ']') {
1034 res.replace(j, 1, "[)]");
1035 }
1036 }
1037
1038 return "'" + res + "'";
1039 }
1040
1041 private:
execute(const formula_callable & variables,formula_debugger * fdb) const1042 variant execute(const formula_callable& variables, formula_debugger*fdb) const
1043 {
1044 if(subs_.empty()) {
1045 return str_;
1046 }
1047
1048 std::string res = str_.as_string();
1049 for(size_t i = 0; i < subs_.size(); ++i) {
1050 const int j = subs_.size() - i - 1;
1051 const substitution& sub = subs_[i];
1052 add_debug_info(fdb, j, "[string subst]");
1053 const std::string str = sub.calculation->evaluate(variables,fdb).string_cast();
1054 res.insert(sub.pos, str);
1055 }
1056
1057 return variant(res);
1058 }
1059
1060 struct substitution
1061 {
substitutionwfl::string_expression::substitution1062 substitution() : pos(0) , calculation() {}
1063
1064 int pos;
1065 const_formula_ptr calculation;
1066 };
1067
1068 variant str_;
1069 std::vector<substitution> subs_;
1070 };
1071
1072
1073 /**
1074 * Functions to handle the actual parsing of WFL.
1075 */
operator_precedence(const tk::token & t)1076 static int operator_precedence(const tk::token& t)
1077 {
1078 static std::map<std::string,int> precedence_map;
1079 if(precedence_map.empty()) {
1080 int n = 0;
1081 precedence_map["not"] = ++n;
1082 precedence_map["where"] = ++n;
1083 precedence_map["or"] = ++n;
1084 precedence_map["and"] = ++n;
1085 precedence_map["="] = ++n;
1086 precedence_map["!="] = n;
1087 precedence_map["<"] = n;
1088 precedence_map[">"] = n;
1089 precedence_map["<="] = n;
1090 precedence_map[">="] = n;
1091 precedence_map["in"] = n;
1092 precedence_map["~"] = ++n;
1093 precedence_map["+"] = ++n;
1094 precedence_map["-"] = n;
1095 precedence_map[".."] = n;
1096 precedence_map["*"] = ++n;
1097 precedence_map["/"] = n;
1098 precedence_map["%"] = ++n;
1099 precedence_map["^"] = ++n;
1100 precedence_map["d"] = ++n;
1101 precedence_map["."] = ++n;
1102 }
1103
1104 assert(precedence_map.count(std::string(t.begin, t.end)));
1105 return precedence_map[std::string(t.begin, t.end)];
1106 }
1107
parse_function_args(const tk::token * & i1,const tk::token * i2,std::vector<std::string> * res)1108 static void parse_function_args(const tk::token* &i1, const tk::token* i2, std::vector<std::string>* res)
1109 {
1110 const tk::token* begin = i1, *end = i2; // These are used for error reporting
1111
1112 if(i1->type == tk::TOKEN_LPARENS) {
1113 ++i1;
1114 } else {
1115 throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1116 }
1117
1118 while((i1-> type != tk::TOKEN_RPARENS) && (i1 != i2)) {
1119 if(i1->type == tk::TOKEN_IDENTIFIER) {
1120 if(std::string((i1+1)->begin, (i1+1)->end) == "*") {
1121 res->push_back(std::string(i1->begin, i1->end) + std::string("*"));
1122 ++i1;
1123 } else {
1124 res->push_back(std::string(i1->begin, i1->end));
1125 }
1126 } else if(i1->type == tk::TOKEN_COMMA) {
1127 //do nothing
1128 } else {
1129 throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1130 }
1131
1132 ++i1;
1133 }
1134
1135 if(i1->type != tk::TOKEN_RPARENS) {
1136 throw formula_error("Invalid function definition", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1137 }
1138
1139 ++i1;
1140 }
1141
parse_args(const tk::token * i1,const tk::token * i2,std::vector<expression_ptr> * res,function_symbol_table * symbols)1142 static void parse_args(const tk::token* i1, const tk::token* i2,
1143 std::vector<expression_ptr>* res,
1144 function_symbol_table* symbols)
1145 {
1146 int parens = 0;
1147 const tk::token* beg = i1;
1148 while(i1 != i2) {
1149 if(i1->type == tk::TOKEN_LPARENS || i1->type == tk::TOKEN_LSQUARE ) {
1150 ++parens;
1151 } else if(i1->type == tk::TOKEN_RPARENS || i1->type == tk::TOKEN_RSQUARE ) {
1152 --parens;
1153 } else if(i1->type == tk::TOKEN_COMMA && !parens) {
1154 res->push_back(parse_expression(beg, i1, symbols));
1155 beg = i1+1;
1156 }
1157
1158 ++i1;
1159 }
1160
1161 if(beg != i1) {
1162 res->push_back(parse_expression(beg, i1, symbols));
1163 }
1164 }
1165
parse_set_args(const tk::token * i1,const tk::token * i2,std::vector<expression_ptr> * res,function_symbol_table * symbols)1166 static void parse_set_args(const tk::token* i1, const tk::token* i2,
1167 std::vector<expression_ptr>* res,
1168 function_symbol_table* symbols)
1169 {
1170 int parens = 0;
1171 bool check_pointer = false;
1172 const tk::token* beg = i1;
1173 const tk::token* begin = i1, *end = i2; // These are used for error reporting
1174 while(i1 != i2) {
1175 if(i1->type == tk::TOKEN_LPARENS || i1->type == tk::TOKEN_LSQUARE) {
1176 ++parens;
1177 } else if(i1->type == tk::TOKEN_RPARENS || i1->type == tk::TOKEN_RSQUARE) {
1178 --parens;
1179 } else if(i1->type == tk::TOKEN_POINTER && !parens ) {
1180 if(!check_pointer) {
1181 check_pointer = true;
1182 res->push_back(parse_expression(beg, i1, symbols));
1183 beg = i1+1;
1184 } else {
1185 throw formula_error("Too many '->' operators found", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1186 }
1187 } else if(i1->type == tk::TOKEN_COMMA && !parens ) {
1188 if(check_pointer)
1189 check_pointer = false;
1190 else {
1191 throw formula_error("Expected comma, but '->' found", tokens_to_string(begin,end - 1), *i1->filename, i1->line_number);
1192 }
1193 res->push_back(parse_expression(beg, i1, symbols));
1194 beg = i1+1;
1195 }
1196
1197 ++i1;
1198 }
1199
1200 if(beg != i1) {
1201 res->push_back(parse_expression(beg, i1, symbols));
1202 }
1203 }
1204
parse_where_clauses(const tk::token * i1,const tk::token * i2,expr_table_ptr res,function_symbol_table * symbols)1205 static void parse_where_clauses(const tk::token* i1, const tk::token* i2, expr_table_ptr res, function_symbol_table* symbols)
1206 {
1207 int parens = 0;
1208 const tk::token* original_i1_cached = i1;
1209 const tk::token* beg = i1;
1210 const tk::token* begin = i1, *end = i2; // These are used for error reporting
1211 std::string var_name;
1212
1213 while(i1 != i2) {
1214 if(i1->type == tk::TOKEN_LPARENS || i1->type == tk::TOKEN_LSQUARE) {
1215 ++parens;
1216 } else if(i1->type == tk::TOKEN_RPARENS || i1->type == tk::TOKEN_RSQUARE) {
1217 --parens;
1218 } else if(!parens) {
1219 if(i1->type == tk::TOKEN_COMMA) {
1220 if(var_name.empty()) {
1221 throw formula_error("There is 'where <expression>' but 'where name=<expression>' was needed",
1222 tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1223 }
1224
1225 (*res)[var_name] = parse_expression(beg, i1, symbols);
1226 beg = i1+1;
1227 var_name = "";
1228 } else if(i1->type == tk::TOKEN_OPERATOR) {
1229 std::string op_name(i1->begin, i1->end);
1230
1231 if(op_name == "=") {
1232 if(beg->type != tk::TOKEN_IDENTIFIER) {
1233 if(i1 == original_i1_cached) {
1234 throw formula_error("There is 'where <expression>' but 'where name=<expression>' was needed",
1235 tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1236 } else {
1237 throw formula_error("There is 'where <expression>=<expression>' but 'where name=<expression>' was needed",
1238 tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1239 }
1240 } else if(beg+1 != i1) {
1241 throw formula_error("There is 'where name <expression>=<expression>' but 'where name=<expression>' was needed",
1242 tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1243 } else if(!var_name.empty()) {
1244 throw formula_error("There is 'where name=name=<expression>' but 'where name=<expression>' was needed",
1245 tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1246 }
1247
1248 var_name.insert(var_name.end(), beg->begin, beg->end);
1249 beg = i1+1;
1250 }
1251 }
1252 }
1253 ++i1;
1254 }
1255
1256 if(beg != i1) {
1257 if(var_name.empty()) {
1258 throw formula_error("There is 'where <expression>' but 'where name=<expression>' was needed",
1259 tokens_to_string(begin, end - 1), *i1->filename, i1->line_number);
1260 }
1261
1262 (*res)[var_name] = parse_expression(beg, i1, symbols);
1263 }
1264 }
1265
parse_expression(const tk::token * i1,const tk::token * i2,function_symbol_table * symbols)1266 expression_ptr parse_expression(const tk::token* i1, const tk::token* i2, function_symbol_table* symbols)
1267 {
1268 if(i1 == i2) {
1269 throw formula_error("Empty expression", "", *i1->filename, i1->line_number);
1270 }
1271
1272 std::unique_ptr<function_symbol_table> temp_functions;
1273 if(!symbols) {
1274 temp_functions.reset(new function_symbol_table(function_symbol_table::get_builtins()));
1275 symbols = temp_functions.get();
1276 }
1277
1278 const tk::token* begin = i1, *end = i2; // These are used for error reporting
1279
1280 if(i1->type == tk::TOKEN_KEYWORD && (i1 + 1)->type == tk::TOKEN_IDENTIFIER) {
1281 if(std::string(i1->begin, i1->end) == "def") {
1282 ++i1;
1283 const std::string formula_name = std::string(i1->begin, i1->end);
1284
1285 std::vector<std::string> args;
1286 parse_function_args(++i1, i2, &args);
1287
1288 const tk::token* beg = i1;
1289 while((i1 != i2) && (i1->type != tk::TOKEN_SEMICOLON)) {
1290 ++i1;
1291 }
1292
1293 const std::string precond = "";
1294 if(symbols == nullptr) {
1295 throw formula_error("Function symbol table required but not present", "",*i1->filename, i1->line_number);
1296 }
1297
1298 symbols->add_function(formula_name,
1299 std::make_shared<user_formula_function>(
1300 formula_name, std::make_shared<const formula>(beg, i1, symbols),
1301 formula::create_optional_formula(precond, symbols), args
1302 )
1303 );
1304
1305 if((i1 == i2) || (i1 == (i2-1))) {
1306 return std::make_shared<function_list_expression>(symbols);
1307 } else {
1308 return parse_expression((i1+1), i2, symbols);
1309 }
1310 }
1311 }
1312
1313 int parens = 0;
1314 const tk::token* op = nullptr;
1315 bool operator_group = false;
1316
1317 for(const tk::token* i = i1; i != i2; ++i) {
1318 if(i->type == tk::TOKEN_LPARENS || i->type == tk::TOKEN_LSQUARE) {
1319 ++parens;
1320 } else if(i->type == tk::TOKEN_RPARENS || i->type == tk::TOKEN_RSQUARE) {
1321 --parens;
1322 } else if(parens == 0 && i->type == tk::TOKEN_OPERATOR) {
1323 if((!operator_group ) && (op == nullptr || operator_precedence(*op) >= operator_precedence(*i))) {
1324 // Need special exception for exponentiation to be right-associative
1325 if(*i->begin != '^' || op == nullptr || *op->begin != '^') {
1326 op = i;
1327 }
1328 }
1329 operator_group = true;
1330 } else {
1331 operator_group = false;
1332 }
1333 }
1334
1335 if(op == nullptr) {
1336 if(i1->type == tk::TOKEN_LPARENS && (i2-1)->type == tk::TOKEN_RPARENS) {
1337 return parse_expression(i1+1,i2-1,symbols);
1338 } else if((i2-1)->type == tk::TOKEN_RSQUARE) { //check if there is [ ] : either a list/map definition, or a operator
1339 // First, a special case for an empty map
1340 if(i2 - i1 == 3 && i1->type == tk::TOKEN_LSQUARE && (i1+1)->type == tk::TOKEN_POINTER) {
1341 return std::make_shared<map_expression>(std::vector<expression_ptr>());
1342 }
1343
1344 const tk::token* tok = i2-2;
1345 int square_parens = 0;
1346 bool is_map = false;
1347 while ((tok->type != tk::TOKEN_LSQUARE || square_parens) && tok != i1) {
1348 if(tok->type == tk::TOKEN_RSQUARE) {
1349 square_parens++;
1350 } else if(tok->type == tk::TOKEN_LSQUARE) {
1351 square_parens--;
1352 } else if((tok->type == tk::TOKEN_POINTER) && !square_parens ) {
1353 is_map = true;
1354 }
1355 --tok;
1356 }
1357
1358 if(tok->type == tk::TOKEN_LSQUARE) {
1359 if(tok == i1) {
1360 // Create a list or a map
1361 std::vector<expression_ptr> args;
1362
1363 if( is_map ) {
1364 parse_set_args(i1+1, i2-1, &args, symbols);
1365 return std::make_shared<map_expression>(args);
1366 } else {
1367 parse_args(i1+1,i2-1,&args,symbols);
1368 return std::make_shared<list_expression>(args);
1369 }
1370 } else {
1371 // Execute operator [ ]
1372 try{
1373 return std::make_shared<square_bracket_expression>(
1374 parse_expression(i1, tok, symbols),
1375 parse_expression(tok + 1, i2 - 1, symbols)
1376 );
1377 } catch (const formula_error& e){
1378 throw formula_error( e.type, tokens_to_string(i1, i2-1), *i1->filename, i1->line_number );
1379 }
1380 }
1381 }
1382 } else if(i2 - i1 == 1) {
1383 if(i1->type == tk::TOKEN_KEYWORD) {
1384 if(std::string(i1->begin, i1->end) == "functions") {
1385 return std::make_shared<function_list_expression>(symbols);
1386 }
1387 } else if(i1->type == tk::TOKEN_IDENTIFIER) {
1388 return std::make_shared<identifier_expression>(std::string(i1->begin, i1->end));
1389 } else if(i1->type == tk::TOKEN_INTEGER) {
1390 int n = std::stoi(std::string(i1->begin, i1->end));
1391 return std::make_shared<integer_expression>(n);
1392 } else if(i1->type == tk::TOKEN_DECIMAL) {
1393 tk::iterator dot = i1->begin;
1394 while(*dot != '.') {
1395 ++dot;
1396 }
1397
1398 int n = std::stoi(std::string(i1->begin,dot));
1399
1400 tk::iterator literal_end = i1->end;
1401
1402 if(literal_end - dot > 4) {
1403 literal_end = dot + 4;
1404 }
1405
1406 ++dot;
1407
1408 int f = 0;
1409
1410 int multiplicator = 100;
1411 while(dot != literal_end) {
1412 f += (*dot - 48) * multiplicator;
1413 multiplicator /= 10;
1414 ++dot;
1415 }
1416
1417 return std::make_shared<decimal_expression>(n, f);
1418 } else if(i1->type == tk::TOKEN_STRING_LITERAL) {
1419 return std::make_shared<string_expression>(std::string(i1->begin + 1, i1->end - 1));
1420 }
1421 } else if(i1->type == tk::TOKEN_IDENTIFIER &&
1422 (i1+1)->type == tk::TOKEN_LPARENS &&
1423 (i2-1)->type == tk::TOKEN_RPARENS)
1424 {
1425 const tk::token* function_call_begin = i1, *function_call_end = i2; // These are used for error reporting
1426 int nleft = 0, nright = 0;
1427 for(const tk::token* i = i1; i != i2; ++i) {
1428 if(i->type == tk::TOKEN_LPARENS) {
1429 ++nleft;
1430 } else if(i->type == tk::TOKEN_RPARENS) {
1431 ++nright;
1432 }
1433 }
1434
1435 if(nleft == nright) {
1436 std::vector<expression_ptr> args;
1437 parse_args(i1+2,i2-1,&args,symbols);
1438 try{
1439 return symbols->create_function(std::string(i1->begin, i1->end),args);
1440 }
1441 catch(const formula_error& e) {
1442 throw formula_error(e.type, tokens_to_string(function_call_begin, function_call_end), *i1->filename, i1->line_number);
1443 }
1444 }
1445 }
1446
1447 throw formula_error("Could not parse expression", tokens_to_string(i1, i2), *i1->filename, i1->line_number);
1448 }
1449
1450 if(op + 1 == end) {
1451 throw formula_error("Expected another token", tokens_to_string(begin, end - 1), *op->filename, op->line_number);
1452 }
1453
1454 if(op == i1) {
1455 try{
1456 return expression_ptr(
1457 new unary_operator_expression(std::string(op->begin, op->end), parse_expression(op + 1, i2 ,symbols)));
1458 } catch(const formula_error& e) {
1459 throw formula_error( e.type, tokens_to_string(begin,end - 1), *op->filename, op->line_number);
1460 }
1461 }
1462
1463 const std::string op_name(op->begin,op->end);
1464
1465 if(op_name == ".") {
1466 return expression_ptr(
1467 new dot_expression(
1468 parse_expression(i1, op, symbols),
1469 parse_expression(op + 1,i2, symbols)
1470 )
1471 );
1472 }
1473
1474 if(op_name == "where") {
1475 expr_table_ptr table(new expr_table());
1476 parse_where_clauses(op+1, i2, table, symbols);
1477
1478 return std::make_shared<where_expression>(parse_expression(i1, op, symbols), table);
1479 }
1480
1481 return expression_ptr(
1482 new operator_expression(op_name,
1483 parse_expression(i1, op, symbols),
1484 parse_expression(op + 1, i2, symbols)
1485 )
1486 );
1487 }
1488
1489 } // namespace wfl
1490