BehaviorTree
Core Library to create and execute Behavior Trees
Loading...
Searching...
No Matches
operators.hpp
1/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved
2*
3* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
4* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
5* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7*
8* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
10* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11*/
12
13#pragma once
14
15#include "behaviortree_cpp/scripting/any_types.hpp"
16#include "behaviortree_cpp/scripting/script_parser.hpp"
17
18#include <cmath>
19#include <memory>
20#include <string>
21#include <vector>
22
23// Naive implementation of an AST with simple evaluation function.
24namespace BT::Ast
25{
26using SimpleString = SafeAny::SimpleString;
27
28using expr_ptr = std::shared_ptr<struct ExprBase>;
29
30// extended string to number that consider enums and booleans
31inline double StringToDouble(const Any& value, const Environment& env)
32{
33 const auto str = value.cast<std::string>();
34 if(str == "true")
35 {
36 return 1.0;
37 }
38 if(str == "false")
39 {
40 return 0.0;
41 }
42 if(env.enums)
43 {
44 auto it = env.enums->find(str);
45 if(it != env.enums->end())
46 {
47 return it->second;
48 }
49 }
50 return value.cast<double>();
51}
52
53struct ExprBase
54{
55 using Ptr = std::shared_ptr<ExprBase>;
56
57 virtual ~ExprBase() = default;
58 virtual Any evaluate(Environment& env) const = 0;
59};
60
61inline std::string ErrorNotInit(const char* side, const char* op_str)
62{
63 return StrCat("The ", side, " operand of the operator [", op_str,
64 "] is not initialized");
65}
66
67struct ExprLiteral : ExprBase
68{
69 Any value;
70
71 ExprLiteral(Any v) : value(v)
72 {}
73
74 Any evaluate(Environment&) const override
75 {
76 return value;
77 }
78};
79
80struct ExprName : ExprBase
81{
82 std::string name;
83
84 explicit ExprName(std::string n) : name(std::move(n))
85 {}
86
87 Any evaluate(Environment& env) const override
88 {
89 //search first in the enums table
90 if(env.enums)
91 {
92 auto enum_ptr = env.enums->find(name);
93 if(enum_ptr != env.enums->end())
94 {
95 return Any(double(enum_ptr->second));
96 }
97 }
98 // search now in the variables table
99 auto any_ref = env.vars->getAnyLocked(name);
100 if(!any_ref)
101 {
102 throw RuntimeError(StrCat("Variable not found: ", name));
103 }
104 return *any_ref.get();
105 }
106};
107
109{
110 enum op_t
111 {
112 negate,
113 complement,
114 logical_not
115 } op;
116 expr_ptr rhs;
117
118 explicit ExprUnaryArithmetic(op_t op, expr_ptr e) : op(op), rhs(std::move(e))
119 {}
120
121 Any evaluate(Environment& env) const override
122 {
123 auto rhs_v = rhs->evaluate(env);
124 if(rhs_v.isNumber())
125 {
126 const double rv = rhs_v.cast<double>();
127 switch(op)
128 {
129 case negate:
130 return Any(-rv);
131 case complement:
132 if(rv > static_cast<double>(std::numeric_limits<int64_t>::max()) ||
133 rv < static_cast<double>(std::numeric_limits<int64_t>::min()))
134 {
135 throw RuntimeError("Number out of range for bitwise operation");
136 }
137 return Any(static_cast<double>(~static_cast<int64_t>(rv)));
138 case logical_not:
139 return Any(static_cast<double>(!static_cast<bool>(rv)));
140 }
141 }
142 else if(rhs_v.isString())
143 {
144 throw RuntimeError("Invalid operator for std::string");
145 }
146 throw RuntimeError("ExprUnaryArithmetic: undefined");
147 }
148};
149
151{
152 enum op_t
153 {
154 plus,
155 minus,
156 times,
157 div,
158 concat,
159
160 bit_and,
161 bit_or,
162 bit_xor,
163
164 logic_and,
165 logic_or
166 } op;
167
168 const char* opStr() const
169 {
170 switch(op)
171 {
172 case plus:
173 return "+";
174 case minus:
175 return "-";
176 case times:
177 return "*";
178 case div:
179 return "/";
180 case concat:
181 return "..";
182 case bit_and:
183 return "&";
184 case bit_or:
185 return "|";
186 case bit_xor:
187 return "^";
188 case logic_and:
189 return "&&";
190 case logic_or:
191 return "||";
192 }
193 return "";
194 }
195
196 expr_ptr lhs, rhs;
197
198 explicit ExprBinaryArithmetic(expr_ptr lhs, op_t op, expr_ptr rhs)
199 : op(op), lhs(std::move(lhs)), rhs(std::move(rhs))
200 {}
201
202 Any evaluate(Environment& env) const override
203 {
204 auto lhs_v = lhs->evaluate(env);
205 auto rhs_v = rhs->evaluate(env);
206
207 if(lhs_v.empty())
208 {
209 throw RuntimeError(ErrorNotInit("left", opStr()));
210 }
211 if(rhs_v.empty())
212 {
213 throw RuntimeError(ErrorNotInit("right", opStr()));
214 }
215
216 if(rhs_v.isNumber() && lhs_v.isNumber())
217 {
218 auto lv = lhs_v.cast<double>();
219 auto rv = rhs_v.cast<double>();
220
221 switch(op)
222 {
223 case plus:
224 return Any(lv + rv);
225 case minus:
226 return Any(lv - rv);
227 case times:
228 return Any(lv * rv);
229 case div:
230 return Any(lv / rv);
231 default: {
232 }
233 }
234
235 if(op == bit_and || op == bit_or || op == bit_xor)
236 {
237 try
238 {
239 int64_t li = lhs_v.cast<int64_t>();
240 int64_t ri = rhs_v.cast<int64_t>();
241 switch(op)
242 {
243 case bit_and:
244 return Any(static_cast<double>(li & ri));
245 case bit_or:
246 return Any(static_cast<double>(li | ri));
247 case bit_xor:
248 return Any(static_cast<double>(li ^ ri));
249 default: {
250 }
251 }
252 }
253 catch(...)
254 {
255 throw RuntimeError("Binary operators are not allowed if "
256 "one of the operands is not an integer");
257 }
258 }
259
260 if(op == logic_or || op == logic_and)
261 {
262 try
263 {
264 auto lb = lhs_v.cast<bool>();
265 auto rb = rhs_v.cast<bool>();
266 switch(op)
267 {
268 case logic_or:
269 return Any(static_cast<double>(lb || rb));
270 case logic_and:
271 return Any(static_cast<double>(lb && rb));
272 default: {
273 }
274 }
275 }
276 catch(...)
277 {
278 throw RuntimeError("Logic operators are not allowed if "
279 "one of the operands is not castable to bool");
280 }
281 }
282 }
283 else if(rhs_v.isString() && lhs_v.isString() && op == plus)
284 {
285 return Any(lhs_v.cast<std::string>() + rhs_v.cast<std::string>());
286 }
287 else if(op == concat && ((rhs_v.isString() && lhs_v.isString()) ||
288 (rhs_v.isString() && lhs_v.isNumber()) ||
289 (rhs_v.isNumber() && lhs_v.isString())))
290 {
291 return Any(lhs_v.cast<std::string>() + rhs_v.cast<std::string>());
292 }
293 else
294 {
295 throw RuntimeError("Operation not permitted");
296 }
297
298 return {}; // unreachable
299 }
300};
301
302template <typename T>
303bool IsSame(const T& lv, const T& rv)
304{
305 if constexpr(std::is_same_v<double, T>)
306 {
307 constexpr double EPS = static_cast<double>(std::numeric_limits<float>::epsilon());
308 return std::abs(lv - rv) <= EPS;
309 }
310 else
311 {
312 return (lv == rv);
313 }
314}
315
317{
318 enum op_t
319 {
320 equal,
321 not_equal,
322 less,
323 greater,
324 less_equal,
325 greater_equal
326 };
327
328 const char* opStr(op_t op) const
329 {
330 switch(op)
331 {
332 case equal:
333 return "==";
334 case not_equal:
335 return "!=";
336 case less:
337 return "<";
338 case greater:
339 return ">";
340 case less_equal:
341 return "<=";
342 case greater_equal:
343 return ">=";
344 }
345 return "";
346 }
347
348 std::vector<op_t> ops;
349 std::vector<expr_ptr> operands;
350
351 Any evaluate(Environment& env) const override
352 {
353 auto SwitchImpl = [&](const auto& lv, const auto& rv, op_t op) {
354 switch(op)
355 {
356 case equal:
357 if(!IsSame(lv, rv))
358 return false;
359 break;
360 case not_equal:
361 if(IsSame(lv, rv))
362 return false;
363 break;
364 case less:
365 if(lv >= rv)
366 return false;
367 break;
368 case greater:
369 if(lv <= rv)
370 return false;
371 break;
372 case less_equal:
373 if(lv > rv)
374 return false;
375 break;
376 case greater_equal:
377 if(lv < rv)
378 return false;
379 break;
380 }
381 return true;
382 };
383
384 auto lhs_v = operands[0]->evaluate(env);
385 for(auto i = 0u; i != ops.size(); ++i)
386 {
387 auto rhs_v = operands[i + 1]->evaluate(env);
388
389 if(lhs_v.empty())
390 {
391 throw RuntimeError(ErrorNotInit("left", opStr(ops[i])));
392 }
393 if(rhs_v.empty())
394 {
395 throw RuntimeError(ErrorNotInit("right", opStr(ops[i])));
396 }
397 const Any False(0.0);
398
399 if(lhs_v.isNumber() && rhs_v.isNumber())
400 {
401 auto lv = lhs_v.cast<double>();
402 auto rv = rhs_v.cast<double>();
403 if(!SwitchImpl(lv, rv, ops[i]))
404 {
405 return False;
406 }
407 }
408 else if(lhs_v.isString() && rhs_v.isString())
409 {
410 auto lv = lhs_v.cast<SimpleString>();
411 auto rv = rhs_v.cast<SimpleString>();
412 if(!SwitchImpl(lv, rv, ops[i]))
413 {
414 return False;
415 }
416 }
417 else if(lhs_v.isString() && rhs_v.isNumber())
418 {
419 auto lv = StringToDouble(lhs_v, env);
420 auto rv = rhs_v.cast<double>();
421 if(!SwitchImpl(lv, rv, ops[i]))
422 {
423 return False;
424 }
425 }
426 else if(lhs_v.isNumber() && rhs_v.isString())
427 {
428 auto lv = lhs_v.cast<double>();
429 auto rv = StringToDouble(rhs_v, env);
430 if(!SwitchImpl(lv, rv, ops[i]))
431 {
432 return False;
433 }
434 }
435 else
436 {
437 throw RuntimeError(StrCat("Can't mix different types in Comparison. "
438 "Left operand [",
439 BT::demangle(lhs_v.type()), "] right operand [",
440 BT::demangle(rhs_v.type()), "]"));
441 }
442 lhs_v = rhs_v;
443 }
444 return Any(1.0);
445 }
446};
447
448struct ExprIf : ExprBase
449{
450 expr_ptr condition, then, else_;
451
452 explicit ExprIf(expr_ptr condition, expr_ptr then, expr_ptr else_)
453 : condition(std::move(condition)), then(std::move(then)), else_(std::move(else_))
454 {}
455
456 Any evaluate(Environment& env) const override
457 {
458 const auto& v = condition->evaluate(env);
459 bool valid = (v.isType<SimpleString>() && v.cast<SimpleString>().size() > 0) ||
460 (v.cast<double>() != 0.0);
461 if(valid)
462 {
463 return then->evaluate(env);
464 }
465 else
466 {
467 return else_->evaluate(env);
468 }
469 }
470};
471
473{
474 enum op_t
475 {
476 assign_create,
477 assign_existing,
478 assign_plus,
479 assign_minus,
480 assign_times,
481 assign_div
482 } op;
483
484 const char* opStr() const
485 {
486 switch(op)
487 {
488 case assign_create:
489 return ":=";
490 case assign_existing:
491 return "=";
492 case assign_plus:
493 return "+=";
494 case assign_minus:
495 return "-=";
496 case assign_times:
497 return "*=";
498 case assign_div:
499 return "/=";
500 }
501 return "";
502 }
503
504 expr_ptr lhs, rhs;
505
506 explicit ExprAssignment(expr_ptr _lhs, op_t op, expr_ptr _rhs)
507 : op(op), lhs(std::move(_lhs)), rhs(std::move(_rhs))
508 {}
509
510 Any evaluate(Environment& env) const override
511 {
512 auto varname = dynamic_cast<ExprName*>(lhs.get());
513 if(!varname)
514 {
515 throw RuntimeError("Assignment left operand not a blackboard entry");
516 }
517 const auto& key = varname->name;
518
519 auto entry = env.vars->getEntry(key);
520 if(!entry)
521 {
522 // variable doesn't exist, create it if using operator assign_create
523 if(op == assign_create)
524 {
525 env.vars->createEntry(key, PortInfo());
526 entry = env.vars->getEntry(key);
527 if(!entry)
528 {
529 throw LogicError("Bug: report");
530 }
531 }
532 else
533 {
534 // fail otherwise
535 auto msg = StrCat("The blackboard entry [", key,
536 "] doesn't exist, yet.\n"
537 "If you want to create a new one, "
538 "use the operator "
539 "[:=] instead of [=]");
540 throw RuntimeError(msg);
541 }
542 }
543 auto value = rhs->evaluate(env);
544
545 std::scoped_lock lock(entry->entry_mutex);
546 auto* dst_ptr = &entry->value;
547
548 auto errorPrefix = [dst_ptr, &key]() {
549 return StrCat("Error assigning a value to entry [", key, "] with type [",
550 BT::demangle(dst_ptr->type()), "]. ");
551 };
552
553 if(value.empty())
554 {
555 throw RuntimeError(ErrorNotInit("right", opStr()));
556 }
557
558 if(op == assign_create || op == assign_existing)
559 {
560 // the very fist assignment can come from any type.
561 // In the future, type check will be done by Any::copyInto
562 if(dst_ptr->empty() && entry->info.type() == typeid(AnyTypeAllowed))
563 {
564 *dst_ptr = value;
565 }
566 else if(value.isString() && !dst_ptr->isString())
567 {
568 // special case: string to other type.
569 // Check if we can use the StringConverter
570 auto const str = value.cast<std::string>();
571 const auto* entry_info = env.vars->entryInfo(key);
572
573 if(auto converter = entry_info->converter())
574 {
575 *dst_ptr = converter(str);
576 }
577 else if(dst_ptr->isNumber())
578 {
579 auto num_value = StringToDouble(value, env);
580 *dst_ptr = Any(num_value);
581 }
582 else
583 {
584 auto msg = StrCat(errorPrefix(),
585 "\nThe right operand is a string, "
586 "can't convert to ",
587 demangle(dst_ptr->type()));
588 throw RuntimeError(msg);
589 }
590 }
591 else
592 {
593 try
594 {
595 value.copyInto(*dst_ptr);
596 }
597 catch(std::exception&)
598 {
599 auto msg = StrCat(errorPrefix(), "\nThe right operand has type [",
600 BT::demangle(value.type()), "] and can't be converted to [",
601 BT::demangle(dst_ptr->type()), "]");
602 throw RuntimeError(msg);
603 }
604 }
605 entry->sequence_id++;
606 entry->stamp = std::chrono::steady_clock::now().time_since_epoch();
607 return *dst_ptr;
608 }
609
610 if(dst_ptr->empty())
611 {
612 throw RuntimeError(ErrorNotInit("left", opStr()));
613 }
614
615 // temporary use
616 Any temp_variable = *dst_ptr;
617
618 if(value.isNumber())
619 {
620 if(!temp_variable.isNumber())
621 {
622 throw RuntimeError("This Assignment operator can't be used "
623 "with a non-numeric type");
624 }
625
626 auto lv = temp_variable.cast<double>();
627 auto rv = value.cast<double>();
628 switch(op)
629 {
630 case assign_plus:
631 temp_variable = Any(lv + rv);
632 break;
633 case assign_minus:
634 temp_variable = Any(lv - rv);
635 break;
636 case assign_times:
637 temp_variable = Any(lv * rv);
638 break;
639 case assign_div:
640 temp_variable = Any(lv / rv);
641 break;
642 default: {
643 }
644 }
645 }
646 else if(value.isString())
647 {
648 if(op == assign_plus)
649 {
650 auto lv = temp_variable.cast<std::string>();
651 auto rv = value.cast<std::string>();
652 temp_variable = Any(lv + rv);
653 }
654 else
655 {
656 throw RuntimeError("Operator not supported for strings");
657 }
658 }
659
660 temp_variable.copyInto(*dst_ptr);
661 entry->sequence_id++;
662 entry->stamp = std::chrono::steady_clock::now().time_since_epoch();
663 return *dst_ptr;
664 }
665};
666} // namespace BT::Ast
667
668namespace BT::Scripting
669{
670
671/// Parse a script string into a list of AST expression nodes.
672/// Throws std::runtime_error on parse failure.
673std::vector<Ast::expr_ptr> parseStatements(const std::string& script);
674
675} // namespace BT::Scripting
Definition: safe_any.hpp:50
Definition: exceptions.h:61
Definition: simple_string.hpp:19
Definition: action_node.h:24
The Environment class is used to encapsulate the information and states needed by the scripting langu...
Definition: script_parser.hpp:32
Definition: operators.hpp:473
Definition: operators.hpp:54
Definition: operators.hpp:151
Definition: operators.hpp:317
Definition: operators.hpp:449
Definition: operators.hpp:68
Definition: operators.hpp:81
Definition: operators.hpp:109