Skip to content

Commit 1ef1c4d

Browse files
committed
add syntax for entries in the root blackboard
1 parent 7771171 commit 1ef1c4d

File tree

6 files changed

+128
-55
lines changed

6 files changed

+128
-55
lines changed

include/behaviortree_cpp/basic_types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ using StringView = std::string_view;
6060

6161
bool StartWith(StringView str, StringView prefix);
6262

63+
bool StartWith(StringView str, char prefix);
64+
6365
// vector of key/value pairs
6466
using KeyValueVector = std::vector<std::pair<std::string, std::string>>;
6567

include/behaviortree_cpp/scripting/any_types.hpp

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (C) 2022 Davide Faconti - All Rights Reserved
1+
/* Copyright (C) 2022-24 Davide Faconti - All Rights Reserved
22
*
33
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
44
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
@@ -12,14 +12,6 @@
1212

1313
#pragma once
1414

15-
#include <cmath>
16-
#include <cstdio>
17-
#include <map>
18-
#include <string>
19-
#include <vector>
20-
#include <utility>
21-
#include <charconv>
22-
2315
#include "lexy/action/parse.hpp"
2416
#include "lexy/callback.hpp"
2517
#include "lexy/dsl.hpp"
@@ -31,6 +23,46 @@ namespace BT::Grammar
3123
{
3224
namespace dsl = lexy::dsl;
3325

26+
struct _xid_start_character : lexyd::char_class_base<_xid_start_character>
27+
{
28+
static LEXY_CONSTEVAL auto char_class_name()
29+
{
30+
return "code-point.BT-start-character";
31+
}
32+
33+
static LEXY_CONSTEVAL auto char_class_ascii()
34+
{
35+
lexy::_detail::ascii_set result;
36+
result.insert('a', 'z');
37+
result.insert('A', 'Z');
38+
result.insert('_');
39+
result.insert('@');
40+
return result;
41+
}
42+
43+
static LEXY_UNICODE_CONSTEXPR bool char_class_match_cp(char32_t cp)
44+
{
45+
// underscore handled as part of ASCII.
46+
return lexy::_detail::code_point_has_properties<LEXY_UNICODE_PROPERTY(xid_start)>(cp);
47+
}
48+
49+
template <typename Encoding>
50+
static constexpr auto char_class_match_swar(lexy::_detail::swar_int c)
51+
{
52+
return lexyd::ascii::_alphau::template char_class_match_swar<Encoding>(c);
53+
}
54+
};
55+
inline constexpr auto xid_start_character = _xid_start_character{};
56+
57+
// A Unicode-aware identifier.
58+
struct Name
59+
{
60+
static constexpr auto rule =
61+
dsl::identifier(xid_start_character, dsl::unicode::xid_continue);
62+
63+
static constexpr auto value = lexy::as_string<std::string>;
64+
};
65+
3466
//----------
3567
struct Integer : lexy::token_production
3668
{

include/behaviortree_cpp/scripting/operators.hpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,10 @@ struct ExprAssignment : ExprBase
510510
{
511511
env.vars->createEntry(key, PortInfo());
512512
entry = env.vars->getEntry(key);
513+
if(!entry)
514+
{
515+
throw LogicError("Bug: report");
516+
}
513517
}
514518
else
515519
{
@@ -550,7 +554,8 @@ struct ExprAssignment : ExprBase
550554
// special case: string to other type.
551555
// Check if we can use the StringConverter
552556
auto const str = value.cast<std::string>();
553-
const auto& entry_info = env.vars->entryInfo(key);
557+
const auto* entry_info = env.vars->entryInfo(key);
558+
554559
if(auto converter = entry_info->converter())
555560
{
556561
*dst_ptr = converter(str);
@@ -648,15 +653,6 @@ namespace dsl = lexy::dsl;
648653

649654
constexpr auto escaped_newline = dsl::backslash >> dsl::newline;
650655

651-
// A Unicode-aware identifier.
652-
struct Name
653-
{
654-
static constexpr auto rule =
655-
dsl::identifier(dsl::unicode::xid_start_underscore, dsl::unicode::xid_continue);
656-
657-
static constexpr auto value = lexy::as_string<std::string>;
658-
};
659-
660656
// An expression that is nested inside another expression.
661657
struct nested_expr : lexy::transparent_production
662658
{

src/basic_types.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,4 +467,9 @@ bool StartWith(StringView str, StringView prefix)
467467
strncmp(str.data(), prefix.data(), prefix.size()) == 0;
468468
}
469469

470+
bool StartWith(StringView str, char prefix)
471+
{
472+
return str.size() >= 1 && str[0] == prefix;
473+
}
474+
470475
} // namespace BT

src/blackboard.cpp

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -45,71 +45,51 @@ Any* Blackboard::getAny(const std::string& key)
4545
const std::shared_ptr<Blackboard::Entry>
4646
Blackboard::getEntry(const std::string& key) const
4747
{
48-
std::unique_lock<std::mutex> lock(mutex_);
49-
auto it = storage_.find(key);
50-
if(it != storage_.end())
48+
// special syntax: "@" will always refer to the root BB
49+
if(StartWith(key, '@'))
5150
{
52-
return it->second;
53-
}
54-
// not found. Try autoremapping
55-
if(auto parent = parent_bb_.lock())
56-
{
57-
auto remap_it = internal_to_external_.find(key);
58-
if(remap_it != internal_to_external_.cend())
51+
if(auto parent = parent_bb_.lock())
5952
{
60-
auto const& new_key = remap_it->second;
61-
return parent->getEntry(new_key);
53+
return parent->getEntry(key);
6254
}
63-
if(autoremapping_ && !IsPrivateKey(key))
55+
else
6456
{
65-
return parent->getEntry(key);
57+
return getEntry(key.substr(1, key.size() - 1));
6658
}
6759
}
68-
return {};
69-
}
7060

71-
std::shared_ptr<Blackboard::Entry> Blackboard::getEntry(const std::string& key)
72-
{
7361
std::unique_lock<std::mutex> lock(mutex_);
7462
auto it = storage_.find(key);
7563
if(it != storage_.end())
7664
{
7765
return it->second;
7866
}
79-
8067
// not found. Try autoremapping
8168
if(auto parent = parent_bb_.lock())
8269
{
8370
auto remap_it = internal_to_external_.find(key);
8471
if(remap_it != internal_to_external_.cend())
8572
{
8673
auto const& new_key = remap_it->second;
87-
auto entry = parent->getEntry(new_key);
88-
if(entry)
89-
{
90-
storage_.insert({ key, entry });
91-
}
92-
return entry;
74+
return parent->getEntry(new_key);
9375
}
9476
if(autoremapping_ && !IsPrivateKey(key))
9577
{
96-
auto entry = parent->getEntry(key);
97-
if(entry)
98-
{
99-
storage_.insert({ key, entry });
100-
}
101-
return entry;
78+
return parent->getEntry(key);
10279
}
10380
}
10481
return {};
10582
}
10683

107-
const TypeInfo* Blackboard::entryInfo(const std::string& key)
84+
std::shared_ptr<Blackboard::Entry> Blackboard::getEntry(const std::string& key)
10885
{
109-
std::unique_lock<std::mutex> lock(mutex_);
86+
return static_cast<const Blackboard&>(*this).getEntry(key);
87+
}
11088

111-
auto it = storage_.find(key);
112-
return (it == storage_.end()) ? nullptr : &(it->second->info);
89+
const TypeInfo* Blackboard::entryInfo(const std::string& key)
90+
{
91+
auto entry = getEntry(key);
92+
return (!entry) ? nullptr : &(entry->info);
11393
}
11494

11595
void Blackboard::addSubtreeRemapping(StringView internal, StringView external)
@@ -167,7 +147,21 @@ std::recursive_mutex& Blackboard::entryMutex() const
167147

168148
void Blackboard::createEntry(const std::string& key, const TypeInfo& info)
169149
{
170-
createEntryImpl(key, info);
150+
if(StartWith(key, '@'))
151+
{
152+
if(auto parent = parent_bb_.lock())
153+
{
154+
parent->createEntry(key, info);
155+
}
156+
else
157+
{
158+
createEntryImpl(key.substr(1, key.size() - 1), info);
159+
}
160+
}
161+
else
162+
{
163+
createEntryImpl(key, info);
164+
}
171165
}
172166

173167
void Blackboard::cloneInto(Blackboard& dst) const

tests/gtest_blackboard.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,3 +557,47 @@ TEST(BlackboardTest, BlackboardBackup)
557557
status = tree.tickWhileRunning();
558558
ASSERT_EQ(status, BT::NodeStatus::SUCCESS);
559559
}
560+
561+
TEST(BlackboardTest, RootBlackboard)
562+
{
563+
BT::BehaviorTreeFactory factory;
564+
565+
const std::string xml_text = R"(
566+
<root BTCPP_format="4" >
567+
<BehaviorTree ID="SubA">
568+
<Sequence>
569+
<SubTree ID="SubB" />
570+
<Script code=" @var3:=3 " />
571+
</Sequence>
572+
</BehaviorTree>
573+
574+
<BehaviorTree ID="SubB">
575+
<Sequence>
576+
<SaySomething message="{@msg}" />
577+
<Script code=" @var4:=4 " />
578+
</Sequence>
579+
</BehaviorTree>
580+
581+
<BehaviorTree ID="MainTree">
582+
<Sequence>
583+
<Script code=" msg:='hello' " />
584+
<SubTree ID="SubA" />
585+
586+
<Script code=" var1:=1 " />
587+
<Script code=" @var2:=2 " />
588+
</Sequence>
589+
</BehaviorTree>
590+
</root> )";
591+
592+
factory.registerNodeType<DummyNodes::SaySomething>("SaySomething");
593+
factory.registerBehaviorTreeFromText(xml_text);
594+
auto tree = factory.createTree("MainTree");
595+
596+
auto status = tree.tickWhileRunning();
597+
ASSERT_EQ(status, BT::NodeStatus::SUCCESS);
598+
599+
ASSERT_EQ(1, tree.rootBlackboard()->get<int>("var1"));
600+
ASSERT_EQ(2, tree.rootBlackboard()->get<int>("var2"));
601+
ASSERT_EQ(3, tree.rootBlackboard()->get<int>("var3"));
602+
ASSERT_EQ(4, tree.rootBlackboard()->get<int>("var4"));
603+
}

0 commit comments

Comments
 (0)