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 <pugixml.hpp>
26 #include <regex>
28 #include <string>
29 #include <unordered_map>
30 
32 {
33 inline namespace reader
34 {
35 template <typename Scope>
36 auto substitute(std::string attribute, Scope & scope)
37 {
38  auto dirname = [](auto &&, auto && scope) { return scope.dirname(); };
39 
40  auto find_pkg_share = [](auto && package_name, auto &&) {
41  return ament_index_cpp::get_package_share_directory(package_name);
42  };
43 
44  auto ros2 = [](auto && arguments, auto &&) {
45  auto remove_trailing_newline = [](auto && s) {
46  while (s.back() == '\n') {
47  s.pop_back();
48  }
49  return s;
50  };
51  if (auto && result = remove_trailing_newline(concealer::dollar("ros2 " + arguments));
52  result.find('\n') != std::string::npos) {
53  throw SyntaxError(
54  "The substitution result by `$(ros2 ...)` must not contain a newline character. "
55  "You gave `$(ros2 ",
56  arguments, ")` and the result was ",
57  std::quoted(boost::replace_all_copy(result, "\n", "\\n")),
58  ", which is unacceptable for the reasons stated above.");
59  } else {
60  return result;
61  }
62  };
63 
64  auto var = [](auto && name, auto && scope) {
65  // TODO: Return the value of the launch configuration variable instead of the OpenSCENARIO parameter.
66  if (const auto found = scope.ref(name); found) {
67  return boost::lexical_cast<String>(found);
68  } else {
69  return String();
70  }
71  };
72 
73  // NOTE: https://design.ros2.org/articles/roslaunch_xml.html#dynamic-configuration
74  static const std::unordered_map<
75  std::string, std::function<std::string(const std::string &, Scope &)> >
76  substitutions{
77  {"dirname", dirname},
78  // TODO {"env", env},
79  // TODO {"eval", eval},
80  // TODO {"exec-in-package", exec_in_package},
81  // TODO {"find-exec", find_exec},
82  // TODO {"find-pkg-prefix", find_pkg_prefix},
83  {"find-pkg-share", find_pkg_share},
84  {"ros2",
85  ros2}, // NOTE: TIER IV extension (Not included in the ROS 2 Launch XML Substitution)
86  {"var", var},
87  };
88 
89  static const auto pattern = std::regex(R"((.*)\$\‍((([\w-]+)\s?([^\)]*))\)(.*))");
90 
91  for (std::smatch result; std::regex_match(attribute, result, pattern);) {
92  if (const auto iter = substitutions.find(result.str(3)); iter != std::end(substitutions)) {
93  attribute = result.str(1) + std::get<1>(*iter)(result.str(4), scope) + result.str(5);
94  } else {
95  throw SyntaxError("Unknown substitution ", std::quoted(result.str(3)), " specified");
96  }
97  }
98 
99  return attribute;
100 }
101 
102 template <typename T, typename Node, typename Scope>
103 auto readAttribute(const std::string & name, const Node & node, const Scope & scope) -> T
104 {
105  auto is_openscenario_standard_expression = [](const auto & s) {
106  return s.substr(0, 2) == "${" and s.back() == '}';
107  };
108 
109  auto read_openscenario_standard_expression = [&](const auto & s) {
110  return boost::lexical_cast<T>(evaluate(std::string(std::begin(s) + 2, std::end(s) - 1), scope));
111  };
112 
113  auto is_openscenario_standard_parameter_reference = [](const auto & s) {
114  return s.front() == '$';
115  };
116 
117  auto read_openscenario_standard_parameter_reference = [&](const auto & s) {
118  // TODO Use `return scope.template ref<T>(s.substr(1));`
119  if (auto && object = scope.ref(s.substr(1)); object) {
120  return boost::lexical_cast<T>(boost::lexical_cast<String>(object));
121  } else {
122  throw SyntaxError(
123  "There is no parameter named ", std::quoted(s.substr(1)), " (Attribute ", std::quoted(name),
124  " of class ", std::quoted(node.name()), " references this parameter)");
125  }
126  };
127 
128  auto read_openscenario_standard_literal = [&](const auto & s) {
129  try {
130  return boost::lexical_cast<T>(s);
131  } catch (const boost::bad_lexical_cast &) {
132  throw SyntaxError(
133  "Value ", std::quoted(s), " specified for attribute ", std::quoted(name),
134  " is invalid (Is not value of type ", makeTypename(typeid(T)), ")");
135  }
136  };
137 
138  // NOTE: https://www.asam.net/index.php?eID=dumpFile&t=f&f=4092&token=d3b6a55e911b22179e3c0895fe2caae8f5492467#_parameters
139 
140  if (const auto & attribute = node.attribute(name.c_str())) {
141  // NOTE: `substitute` is TIER IV extension (Non-OpenSCENARIO standard)
142  if (std::string value = substitute(attribute.value(), scope); value.empty()) {
143  return T();
144  } else if (is_openscenario_standard_expression(value)) {
145  return read_openscenario_standard_expression(value);
146  } else if (is_openscenario_standard_parameter_reference(value)) {
147  return read_openscenario_standard_parameter_reference(value);
148  } else {
149  return read_openscenario_standard_literal(value);
150  }
151  } else {
152  throw SyntaxError(
153  "Required attribute ", std::quoted(name), " not specified for class ",
154  std::quoted(node.name()));
155  }
156 }
157 
158 template <typename T, typename Node, typename Scope>
159 auto readAttribute(const std::string & name, const Node & node, const Scope & scope, T && value)
160 {
161  if (node.attribute(name.c_str())) {
162  return readAttribute<T>(name, node, scope);
163  } else {
164  return value;
165  }
166 }
167 } // namespace reader
168 } // namespace openscenario_interpreter
169 
170 #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:56
auto substitute(std::string attribute, Scope &scope)
Definition: attribute.hpp:36
auto readAttribute(const std::string &name, const Node &node, const Scope &scope) -> T
Definition: attribute.hpp:103
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