From 1ef1c4d278c636c7183bf596356723cfd60876f0 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Fri, 5 Apr 2024 16:54:39 +0200 Subject: [PATCH 1/4] add syntax for entries in the root blackboard --- include/behaviortree_cpp/basic_types.h | 2 + .../behaviortree_cpp/scripting/any_types.hpp | 50 +++++++++++--- .../behaviortree_cpp/scripting/operators.hpp | 16 ++--- src/basic_types.cpp | 5 ++ src/blackboard.cpp | 66 +++++++++---------- tests/gtest_blackboard.cpp | 44 +++++++++++++ 6 files changed, 128 insertions(+), 55 deletions(-) diff --git a/include/behaviortree_cpp/basic_types.h b/include/behaviortree_cpp/basic_types.h index 18a3a931c..ffa14b3c8 100644 --- a/include/behaviortree_cpp/basic_types.h +++ b/include/behaviortree_cpp/basic_types.h @@ -60,6 +60,8 @@ using StringView = std::string_view; bool StartWith(StringView str, StringView prefix); +bool StartWith(StringView str, char prefix); + // vector of key/value pairs using KeyValueVector = std::vector>; diff --git a/include/behaviortree_cpp/scripting/any_types.hpp b/include/behaviortree_cpp/scripting/any_types.hpp index a757bec7f..0db106288 100644 --- a/include/behaviortree_cpp/scripting/any_types.hpp +++ b/include/behaviortree_cpp/scripting/any_types.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-24 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, @@ -12,14 +12,6 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include - #include "lexy/action/parse.hpp" #include "lexy/callback.hpp" #include "lexy/dsl.hpp" @@ -31,6 +23,46 @@ namespace BT::Grammar { namespace dsl = lexy::dsl; +struct _xid_start_character : lexyd::char_class_base<_xid_start_character> +{ + static LEXY_CONSTEVAL auto char_class_name() + { + return "code-point.BT-start-character"; + } + + static LEXY_CONSTEVAL auto char_class_ascii() + { + lexy::_detail::ascii_set result; + result.insert('a', 'z'); + result.insert('A', 'Z'); + result.insert('_'); + result.insert('@'); + return result; + } + + static LEXY_UNICODE_CONSTEXPR bool char_class_match_cp(char32_t cp) + { + // underscore handled as part of ASCII. + return lexy::_detail::code_point_has_properties(cp); + } + + template + static constexpr auto char_class_match_swar(lexy::_detail::swar_int c) + { + return lexyd::ascii::_alphau::template char_class_match_swar(c); + } +}; +inline constexpr auto xid_start_character = _xid_start_character{}; + +// A Unicode-aware identifier. +struct Name +{ + static constexpr auto rule = + dsl::identifier(xid_start_character, dsl::unicode::xid_continue); + + static constexpr auto value = lexy::as_string; +}; + //---------- struct Integer : lexy::token_production { diff --git a/include/behaviortree_cpp/scripting/operators.hpp b/include/behaviortree_cpp/scripting/operators.hpp index b38132d17..176fa2df0 100644 --- a/include/behaviortree_cpp/scripting/operators.hpp +++ b/include/behaviortree_cpp/scripting/operators.hpp @@ -510,6 +510,10 @@ struct ExprAssignment : ExprBase { env.vars->createEntry(key, PortInfo()); entry = env.vars->getEntry(key); + if(!entry) + { + throw LogicError("Bug: report"); + } } else { @@ -550,7 +554,8 @@ struct ExprAssignment : ExprBase // special case: string to other type. // Check if we can use the StringConverter auto const str = value.cast(); - const auto& entry_info = env.vars->entryInfo(key); + const auto* entry_info = env.vars->entryInfo(key); + if(auto converter = entry_info->converter()) { *dst_ptr = converter(str); @@ -648,15 +653,6 @@ namespace dsl = lexy::dsl; constexpr auto escaped_newline = dsl::backslash >> dsl::newline; -// A Unicode-aware identifier. -struct Name -{ - static constexpr auto rule = - dsl::identifier(dsl::unicode::xid_start_underscore, dsl::unicode::xid_continue); - - static constexpr auto value = lexy::as_string; -}; - // An expression that is nested inside another expression. struct nested_expr : lexy::transparent_production { diff --git a/src/basic_types.cpp b/src/basic_types.cpp index 8265365b8..ad7f7199c 100644 --- a/src/basic_types.cpp +++ b/src/basic_types.cpp @@ -467,4 +467,9 @@ bool StartWith(StringView str, StringView prefix) strncmp(str.data(), prefix.data(), prefix.size()) == 0; } +bool StartWith(StringView str, char prefix) +{ + return str.size() >= 1 && str[0] == prefix; +} + } // namespace BT diff --git a/src/blackboard.cpp b/src/blackboard.cpp index eecc409d6..b52d3d4ec 100644 --- a/src/blackboard.cpp +++ b/src/blackboard.cpp @@ -45,38 +45,25 @@ Any* Blackboard::getAny(const std::string& key) const std::shared_ptr Blackboard::getEntry(const std::string& key) const { - std::unique_lock lock(mutex_); - auto it = storage_.find(key); - if(it != storage_.end()) + // special syntax: "@" will always refer to the root BB + if(StartWith(key, '@')) { - return it->second; - } - // not found. Try autoremapping - if(auto parent = parent_bb_.lock()) - { - auto remap_it = internal_to_external_.find(key); - if(remap_it != internal_to_external_.cend()) + if(auto parent = parent_bb_.lock()) { - auto const& new_key = remap_it->second; - return parent->getEntry(new_key); + return parent->getEntry(key); } - if(autoremapping_ && !IsPrivateKey(key)) + else { - return parent->getEntry(key); + return getEntry(key.substr(1, key.size() - 1)); } } - return {}; -} -std::shared_ptr Blackboard::getEntry(const std::string& key) -{ std::unique_lock lock(mutex_); auto it = storage_.find(key); if(it != storage_.end()) { return it->second; } - // not found. Try autoremapping if(auto parent = parent_bb_.lock()) { @@ -84,32 +71,25 @@ std::shared_ptr Blackboard::getEntry(const std::string& key) if(remap_it != internal_to_external_.cend()) { auto const& new_key = remap_it->second; - auto entry = parent->getEntry(new_key); - if(entry) - { - storage_.insert({ key, entry }); - } - return entry; + return parent->getEntry(new_key); } if(autoremapping_ && !IsPrivateKey(key)) { - auto entry = parent->getEntry(key); - if(entry) - { - storage_.insert({ key, entry }); - } - return entry; + return parent->getEntry(key); } } return {}; } -const TypeInfo* Blackboard::entryInfo(const std::string& key) +std::shared_ptr Blackboard::getEntry(const std::string& key) { - std::unique_lock lock(mutex_); + return static_cast(*this).getEntry(key); +} - auto it = storage_.find(key); - return (it == storage_.end()) ? nullptr : &(it->second->info); +const TypeInfo* Blackboard::entryInfo(const std::string& key) +{ + auto entry = getEntry(key); + return (!entry) ? nullptr : &(entry->info); } void Blackboard::addSubtreeRemapping(StringView internal, StringView external) @@ -167,7 +147,21 @@ std::recursive_mutex& Blackboard::entryMutex() const void Blackboard::createEntry(const std::string& key, const TypeInfo& info) { - createEntryImpl(key, info); + if(StartWith(key, '@')) + { + if(auto parent = parent_bb_.lock()) + { + parent->createEntry(key, info); + } + else + { + createEntryImpl(key.substr(1, key.size() - 1), info); + } + } + else + { + createEntryImpl(key, info); + } } void Blackboard::cloneInto(Blackboard& dst) const diff --git a/tests/gtest_blackboard.cpp b/tests/gtest_blackboard.cpp index 980db1e90..98eaa8c99 100644 --- a/tests/gtest_blackboard.cpp +++ b/tests/gtest_blackboard.cpp @@ -557,3 +557,47 @@ TEST(BlackboardTest, BlackboardBackup) status = tree.tickWhileRunning(); ASSERT_EQ(status, BT::NodeStatus::SUCCESS); } + +TEST(BlackboardTest, RootBlackboard) +{ + BT::BehaviorTreeFactory factory; + + const std::string xml_text = R"( + + + + +