TAMSVIZ
Visualization and annotation tool for ROS
mquery.cpp
1 // TAMSVIZ
2 // (c) 2020 Philipp Ruppel
3 
4 #include "mquery.h"
5 
6 #include <regex>
7 
8 void MessageQuery::tokenize() {
9  LOG_DEBUG("tokenize");
10 
11  _tokens.clear();
12  _tok_ok = true;
13 
14  std::string::const_iterator current = _query.begin();
15  std::string::const_iterator end = _query.end();
16 
17  static std::vector<std::pair<TokenType, std::regex>> expression_map = {
18  {TokenType::Space, std::regex("\\s+")},
19  {TokenType::Symbol, std::regex("[A-Za-z_][A-Za-z0-9_]*")},
20  {TokenType::Dot, std::regex("\\.")},
21  {TokenType::String, std::regex("\\\"[^\\\"]*\\\"|\\'[^\\']*\\'")},
22  {TokenType::Integer, std::regex("[0-9]+")},
23  {TokenType::IndexBegin, std::regex("\\[")},
24  {TokenType::IndexEnd, std::regex("\\]")},
25  {TokenType::Equality, std::regex("\\=\\=?")},
26  {TokenType::Inequality, std::regex("\\!\\=")},
27  {TokenType::GreaterThan, std::regex("\\>")},
28  {TokenType::LessThan, std::regex("\\<")},
29  {TokenType::GreaterEqual, std::regex("\\>\\=")},
30  {TokenType::LessEqual, std::regex("\\<\\=")},
31  {TokenType::Double,
32  std::regex("[-+]?([0-9]*\\.)?[0-9]+([eE][-+]?[0-9]+)?")},
33  };
34 
35  std::smatch match;
36  while (current != end) {
37  TokenInfo token;
38  for (auto &expression_mapping : expression_map) {
39  auto &token_type = expression_mapping.first;
40  auto &expression = expression_mapping.second;
41 
42  bool ok = std::regex_search(current, end, match, expression,
43  std::regex_constants::match_continuous);
44  if (ok) {
45  token.type = token_type;
46  token.str = match[0];
47 
48  if (token.type == TokenType::Double ||
49  token.type == TokenType::Integer) {
50  try {
51  token.number = std::stod(token.str);
52  } catch (const std::invalid_argument &ex) {
53  token.type = TokenType::None;
54  break;
55  } catch (const std::out_of_range &ex) {
56  token.type = TokenType::None;
57  break;
58  }
59  }
60 
61  if (token.type == TokenType::Integer) {
62  try {
63  token.integer = std::stoull(token.str);
64  } catch (const std::invalid_argument &ex) {
65  token.type = TokenType::None;
66  break;
67  } catch (const std::out_of_range &ex) {
68  token.type = TokenType::None;
69  break;
70  }
71  }
72 
73  if (token.type == TokenType::String) {
74  token.str = token.str.substr(1, token.str.size() - 2);
75  }
76 
77  break;
78  }
79  }
80 
81  token.begin = current - _query.begin();
82  token.end = token.begin + match[0].length();
83 
84  if (token.type != TokenType::None) {
85  if (token.type != TokenType::Space) {
86  _tokens.push_back(token);
87  }
88  current += match[0].length();
89  } else {
90  //_tokens.clear();
91  _tok_ok = false;
92  break;
93  }
94  }
95 
96  for (auto &tok : _tokens) {
97  LOG_DEBUG("token " << (int)tok.type << " " << tok.str);
98  }
99 }
100 
101 bool MessageQuery::filter(const MessageParser &parser, const TokenInfo &op,
102  const TokenInfo &val) const {
103 
104  if (val.type == TokenType::Integer || val.type == TokenType::Double) {
105  switch (op.type) {
106 
107  case TokenType::Equality:
108  return parser.toDouble() == val.number;
109  break;
110 
111  case TokenType::Inequality:
112  return parser.toDouble() != val.number;
113  break;
114 
115  case TokenType::LessThan:
116  return parser.toDouble() < val.number;
117  break;
118 
119  case TokenType::GreaterThan:
120  return parser.toDouble() > val.number;
121  break;
122 
123  case TokenType::LessEqual:
124  return parser.toDouble() <= val.number;
125  break;
126 
127  case TokenType::GreaterEqual:
128  return parser.toDouble() >= val.number;
129  break;
130  }
131  }
132 
133  if (val.type == TokenType::String) {
134  switch (op.type) {
135 
136  case TokenType::Equality:
137  return parser.toString() == val.str;
138  break;
139 
140  case TokenType::Inequality:
141  return parser.toString() != val.str;
142  break;
143  }
144  }
145 
146  return false;
147 }
148 
149 bool MessageQuery::filter(
150  const MessageParser &parser, TokenIterator &current_token,
151  const std::function<void(const TokenIterator &, const std::string &)>
152  &complete) const {
153 
154  MessageParser msg = query(parser, current_token, complete);
155 
156  if (complete) {
157  if (msg.isPrimitive()) {
158  complete(current_token, "==");
159  complete(current_token, "!=");
160  if (!parser.isString()) {
161  complete(current_token, "<");
162  complete(current_token, ">");
163  complete(current_token, "<=");
164  complete(current_token, ">=");
165  }
166  }
167  }
168 
169  if (current_token == _tokens.end()) {
170  return false;
171  }
172  TokenInfo op = *current_token;
173  current_token++;
174 
175  if (complete) {
176  if (msg.isPrimitive()) {
177  auto str = msg.toString();
178  if (msg.isString()) {
179  if (str.find("\"") == std::string::npos) {
180  complete(current_token, "\"" + str + "\"");
181  }
182  } else {
183  complete(current_token, str);
184  }
185  }
186  }
187 
188  if (current_token == _tokens.end()) {
189  return false;
190  }
191  TokenInfo val = *current_token;
192  current_token++;
193 
194  if (complete) {
195  complete(current_token, "]");
196  }
197 
198  return filter(msg, op, val);
199 }
200 
201 MessageParser MessageQuery::query(
202  MessageParser parser, TokenIterator &current_token,
203  const std::function<void(const TokenIterator &, const std::string &)>
204  &complete) const {
205 
206  if (!parser.isMessage()) {
207  return MessageParser();
208  }
209 
210  if (complete) {
211  for (size_t i = 0; i < parser.size(); i++) {
212  complete(current_token, parser.fieldName(i));
213  }
214  }
215 
216  if (current_token == _tokens.end() ||
217  current_token->type != TokenType::Symbol) {
218  return MessageParser();
219  }
220 
221  parser = parser[current_token->str];
222  ++current_token;
223 
224  while (true) {
225 
226  if (parser.isMessage()) {
227 
228  if (complete) {
229  for (size_t i = 0; i < parser.size(); i++) {
230  complete(current_token, "." + parser.fieldName(i));
231  }
232  }
233 
234  if (current_token != _tokens.end() &&
235  current_token->type == TokenType::Dot &&
236  (current_token + 1) != _tokens.end() &&
237  (current_token + 1)->type == TokenType::Symbol) {
238 
239  parser = parser[(current_token + 1)->str];
240  current_token += 2;
241 
242  continue;
243  }
244  }
245 
246  if (parser.isArray()) {
247 
248  if (complete) {
249  complete(current_token, "[");
250  for (size_t i = 0; i <= 3 && i < parser.size(); i++) {
251  complete(current_token, "[" + std::to_string(i) + "]");
252  }
253  }
254 
255  if (current_token != _tokens.end() &&
256  current_token->type == TokenType::IndexBegin) {
257 
258  if ((current_token + 1) != _tokens.end() &&
259  (current_token + 1)->type == TokenType::Integer) {
260 
261  if (complete) {
262  complete(current_token + 2, "]");
263  }
264 
265  if ((current_token + 2) != _tokens.end() &&
266  (current_token + 2)->type == TokenType::IndexEnd) {
267 
268  parser = parser[(current_token + 1)->integer];
269  current_token += 3;
270 
271  continue;
272  }
273  }
274 
275  size_t array_size = parser.size();
276  bool found = false;
277  for (size_t i = 0; i < array_size; i++) {
278 
279  auto element = parser[i];
280  TokenIterator filter_token = current_token + 1;
281  if (filter(element, filter_token, complete)) {
282  current_token = filter_token;
283 
284  if (current_token == _tokens.end() ||
285  current_token->type != TokenType::IndexEnd) {
286  return MessageParser();
287  }
288  current_token++;
289 
290  parser = element;
291 
292  found = true;
293 
294  break;
295  }
296  }
297  if (found) {
298  continue;
299  } else {
300  break;
301  }
302  }
303  }
304 
305  break;
306  }
307  return parser;
308 }
309 
310 MessageQuery::MessageQuery() {}
311 
312 MessageQuery::MessageQuery(const std::string &query) { assign(query); }
313 
314 void MessageQuery::assign(const std::string &query) {
315  if (query != _query) {
316  _query = query;
317  tokenize();
318  }
319 }
320 
321 MessageParser MessageQuery::operator()(const MessageParser &parser) const {
322  PROFILER("MessageQuery");
323  if (!_tok_ok) {
324  return MessageParser();
325  }
326  TokenIterator current_token = _tokens.begin();
327  auto ret = query(parser, current_token);
328  if (current_token == _tokens.end()) {
329  // LOG_DEBUG(ret.print());
330  return ret;
331  } else {
332  return MessageParser();
333  }
334 }
335 
336 void MessageQuery::complete(const MessageParser &parser,
337  AutoCompletion &completion) const {
338  LOG_DEBUG("complete query");
339  LOG_DEBUG("query " << _query);
340  std::set<std::string> ret;
341  TokenIterator current_token = _tokens.begin();
342  auto value =
343  query(parser, current_token,
344  [&](const TokenIterator &token, const std::string &completion) {
345  std::string prefix;
346  if (token != _tokens.begin()) {
347  TokenIterator tok = token;
348  tok--;
349  LOG_DEBUG("token end " << tok->end);
350  prefix = _query.substr(0, tok->end);
351  }
352  LOG_DEBUG("completion " << prefix << completion);
353  ret.insert(prefix + completion);
354  });
355  completion.completed = value.isPrimitive();
356  LOG_DEBUG("completed " << (completion.completed ? "true" : "false"));
357  completion.items.assign(ret.begin(), ret.end());
358 }