scenario_simulator_v2 C++ API
attribute.hpp
Go to the documentation of this file.
1 // Copyright 2015 TIER IV, Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef OPENSCENARIO_INTERPRETER__READER__ATTRIBUTE_HPP_
16 #define OPENSCENARIO_INTERPRETER__READER__ATTRIBUTE_HPP_
17 
18 #include <ament_index_cpp/get_package_share_directory.hpp>
19 #include <boost/algorithm/string/replace.hpp>
20 #include <concealer/execute.hpp>
21 #include <functional>
25 #include <optional>
26 #include <pugixml.hpp>
27 #include <regex>
29 #include <string>
30 #include <unordered_map>
31 
33 {
34 inline namespace reader
35 {
36 template <typename Scope>
37 auto substitute(std::string attribute, Scope & scope)
38 {
39  auto dirname = [](auto &&, auto && scope) { return scope.dirname(); };
40 
41  auto find_pkg_share = [](auto && package_name, auto &&) {
42  return ament_index_cpp::get_package_share_directory(package_name);
43  };
44 
45  auto ros2 = [](auto && arguments, auto &&) {
46  auto remove_trailing_newline = [](auto && s) {
47  while (s.back() == '\n') {
48  s.pop_back();
49  }
50  return s;
51  };
52  if (auto && result = remove_trailing_newline(concealer::dollar("ros2 " + arguments));
53  result.find('\n') != std::string::npos) {
54  throw SyntaxError(
55  "The substitution result by `$(ros2 ...)` must not contain a newline character. "
56  "You gave `$(ros2 ",
57  arguments, ")` and the result was ",
58  std::quoted(boost::replace_all_copy(result, "\n", "\\n")),
59  ", which is unacceptable for the reasons stated above.");
60  } else {
61  return result;
62  }
63  };
64 
65  auto var = [](auto && name, auto && scope) {
66  // TODO: Return the value of the launch configuration variable instead of the OpenSCENARIO parameter.
67  if (const auto found = scope.ref(name); found) {
68  return boost::lexical_cast<String>(found);
69  } else {
70  return String();
71  }
72  };
73 
74  // NOTE: https://design.ros2.org/articles/roslaunch_xml.html#dynamic-configuration
75  static const std::unordered_map<
76  std::string, std::function<std::string(const std::string &, Scope &)> >
77  substitutions{
78  {"dirname", dirname},
79  // TODO {"env", env},
80  // TODO {"eval", eval},
81  // TODO {"exec-in-package", exec_in_package},
82  // TODO {"find-exec", find_exec},
83  // TODO {"find-pkg-prefix", find_pkg_prefix},
84  {"find-pkg-share", find_pkg_share},
85  {"ros2",
86  ros2}, // NOTE: TIER IV extension (Not included in the ROS 2 Launch XML Substitution)
87  {"var", var},
88  };
89 
90  static const auto pattern = std::regex(R"((.*)\$\‍((([\w-]+)\s?([^\)]*))\)(.*))");
91 
92  for (std::smatch result; std::regex_match(attribute, result, pattern);) {
93  if (const auto iter = substitutions.find(result.str(3)); iter != std::end(substitutions)) {
94  attribute = result.str(1) + std::get<1>(*iter)(result.str(4), scope) + result.str(5);
95  } else {
96  throw SyntaxError("Unknown substitution ", std::quoted(result.str(3)), " specified");
97  }
98  }
99 
100  return attribute;
101 }
102 
103 template <typename T, typename Node, typename Scope>
104 auto readAttribute(const std::string & name, const Node & node, const Scope & scope) -> T
105 {
106  auto is_openscenario_standard_expression = [](const auto & s) {
107  return s.substr(0, 2) == "${" and s.back() == '}';
108  };
109 
110  auto read_openscenario_standard_expression = [&](const auto & s) {
111  return boost::lexical_cast<T>(evaluate(std::string(std::begin(s) + 2, std::end(s) - 1), scope));
112  };
113 
114  auto is_openscenario_standard_parameter_reference = [](const auto & s) {
115  return s.front() == '$';
116  };
117 
118  auto read_openscenario_standard_parameter_reference = [&](const auto & s) {
119  // TODO Use `return scope.template ref<T>(s.substr(1));`
120  if (auto && object = scope.ref(s.substr(1)); object) {
121  return boost::lexical_cast<T>(boost::lexical_cast<String>(object));
122  } else {
123  throw SyntaxError(
124  "There is no parameter named ", std::quoted(s.substr(1)), " (Attribute ", std::quoted(name),
125  " of class ", std::quoted(node.name()), " references this parameter)");
126  }
127  };
128 
129  auto read_openscenario_standard_literal = [&](const auto & s) {
130  try {
131  return boost::lexical_cast<T>(s);
132  } catch (const boost::bad_lexical_cast &) {
133  throw SyntaxError(
134  "Value ", std::quoted(s), " specified for attribute ", std::quoted(name),
135  " is invalid (Is not value of type ", makeTypename(typeid(T)), ")");
136  }
137  };
138 
139  // NOTE: https://www.asam.net/index.php?eID=dumpFile&t=f&f=4092&token=d3b6a55e911b22179e3c0895fe2caae8f5492467#_parameters
140 
141  if (const auto & attribute = node.attribute(name.c_str())) {
142  // NOTE: `substitute` is TIER IV extension (Non-OpenSCENARIO standard)
143  if (std::string value = substitute(attribute.value(), scope); value.empty()) {
144  return T();
145  } else if (is_openscenario_standard_expression(value)) {
146  return read_openscenario_standard_expression(value);
147  } else if (is_openscenario_standard_parameter_reference(value)) {
148  return read_openscenario_standard_parameter_reference(value);
149  } else {
150  return read_openscenario_standard_literal(value);
151  }
152  } else {
153  throw SyntaxError(
154  "Required attribute ", std::quoted(name), " not specified for class ",
155  std::quoted(node.name()));
156  }
157 }
158 
159 template <typename T, typename Node, typename Scope>
160 auto readAttribute(const std::string & name, const Node & node, const Scope & scope, T && value)
161 {
162  if (node.attribute(name.c_str())) {
163  return readAttribute<T>(name, node, scope);
164  } else {
165  return value;
166  }
167 }
168 
169 template <typename T, typename Node, typename Scope>
170 auto readAttribute(const std::string & name, const Node & node, const Scope & scope, std::nullopt_t)
171 {
172  if (node.attribute(name.c_str())) {
173  return std::make_optional(readAttribute<T>(name, node, scope));
174  } else {
175  return std::optional<T>();
176  }
177 }
178 } // namespace reader
179 } // namespace openscenario_interpreter
180 
181 #endif // OPENSCENARIO_INTERPRETER__READER__ATTRIBUTE_HPP_
Definition: scope.hpp:154
auto dirname() const -> std::string
Definition: scope.cpp:107
auto ref(Ts &&... xs) const -> decltype(auto)
Definition: scope.hpp:210
auto dollar(const std::string &command) -> std::string
Definition: execute.cpp:57
auto substitute(std::string attribute, Scope &scope)
Definition: attribute.hpp:37
auto readAttribute(const std::string &name, const Node &node, const Scope &scope) -> T
Definition: attribute.hpp:104
std::string evaluate(const std::string &, const Scope &)
Definition: evaluate.cpp:237
std::string String
Definition: string.hpp:24
auto makeTypename(Ts &&... xs)
Definition: demangle.hpp:30
Definition: escape_sequence.hpp:22
std::string string
Definition: junit5.hpp:26