ROS 2 rclcpp + rcl - rolling  rolling-e7363f10
ROS 2 C++ Client Library with ROS Client Library
lexer.c
1 // Copyright 2018 Open Source Robotics Foundation, Inc.
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 #include <stddef.h>
16 
17 #include "rcl/error_handling.h"
18 #include "rcl/lexer.h"
19 #include "rcl/types.h"
20 
21 /* The lexer tries to find a lexeme in a string.
22  * It looks at one character at a time, and uses that character's value to decide how to transition
23  * a state machine.
24  * A transition is taken if a character's ASCII value falls within its range.
25  * There is never more than one matching transition.
26  *
27  * If no transition matches then it uses a state's '<else,M>' transition.
28  * Every state has exactly one '<else,M>' transition.
29  * In the diagram below all states have an `<else,0>` to T_NONE unless otherwise specified.
30  *
31  * When a transition is taken it causes the lexer to move to another character in the string.
32  * Normal transitions always move the lexer forwards one character.
33  * '<else,M>' transitions may cause the lexer to move forwards 1, or backwards N.
34  * The movement M is written as M = 1 + N so it can be stored in an unsigned integer.
35  * For example, an `<else>` transition with M = 0 moves the lexer forwards 1 character, M = 1 keeps
36  * the lexer at the current character, and M = 2 moves the lexer backwards one character.
37 
38 digraph remapping_lexer {
39  rankdir=LR;
40  node [shape = box, fontsize = 7];
41  T_TILDE_SLASH
42  T_URL_SERVICE
43  T_URL_TOPIC
44  T_COLON
45  T_NODE
46  T_NS
47  T_SEPARATOR
48  T_BR1
49  T_BR2
50  T_BR3
51  T_BR4
52  T_BR5
53  T_BR6
54  T_BR7
55  T_BR8
56  T_BR9
57  T_TOKEN
58  T_FORWARD_SLASH
59  T_WILD_ONE
60  T_WILD_MULTI
61  T_EOF
62  T_NONE
63  T_DOT
64  node [shape = circle];
65  S0 -> T_FORWARD_SLASH [ label = "/"];
66  S0 -> T_DOT [ label = "."];
67  S0 -> S1 [ label = "\\"];
68  S0 -> S2 [ label = "~"];
69  S0 -> S3 [ label = "_" ];
70  S0 -> S9 [ label = "a-qs-zA-Z"];
71  S0 -> S11 [ label = "r"];
72  S0 -> S30 [ label = "*"];
73  S0 -> S31 [ label = ":"];
74  S1 -> T_BR1 [ label = "1"];
75  S1 -> T_BR2 [ label = "2"];
76  S1 -> T_BR3 [ label = "3"];
77  S1 -> T_BR4 [ label = "4"];
78  S1 -> T_BR5 [ label = "5"];
79  S1 -> T_BR6 [ label = "6"];
80  S1 -> T_BR7 [ label = "7"];
81  S1 -> T_BR8 [ label = "8"];
82  S1 -> T_BR9 [ label = "9"];
83  S2 -> T_TILDE_SLASH [ label ="/" ];
84  S3 -> S4 [ label = "_" ];
85  S3 -> S10 [ label = "<else,1>", color = crimson, fontcolor = crimson];
86  S4 -> S5 [ label = "n" ];
87  S5 -> T_NS [ label = "s"];
88  S5 -> S6 [ label = "o" ];
89  S6 -> S8 [ label = "d" ];
90  S5 -> S7 [ label = "a" ];
91  S7 -> S8 [ label = "m" ];
92  S8 -> T_NODE [ label = "e"];
93  S9 -> T_TOKEN [ label = "<else,1>", color=crimson, fontcolor=crimson];
94  S9 -> S9 [ label = "a-zA-Z0-9"];
95  S9 -> S10 [ label = "_"];
96  S10 -> T_TOKEN [ label = "<else,1>", color=crimson, fontcolor=crimson];
97  S10 -> S9 [ label = "a-zA-Z0-9"];
98  S11 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
99  S11 -> S12 [ label = "o"];
100  S12 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
101  S12 -> S13 [ label = "s"];
102  S13 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
103  S13 -> S14 [ label = "t"];
104  S13 -> S21 [ label = "s"];
105  S14 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
106  S14 -> S15 [ label = "o"];
107  S15 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
108  S15 -> S16 [ label = "p"];
109  S16 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
110  S16 -> S17 [ label = "i"];
111  S17 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
112  S17 -> S18 [ label = "c"];
113  S18 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
114  S18 -> S19 [ label = ":"];
115  S19 -> S20 [ label = "/"];
116  S19 -> S9 [ label = "<else,2>", color=crimson, fontcolor=crimson];
117  S20 -> T_URL_TOPIC [ label = "/"];
118  S20 -> S9 [ label = "<else,3>", color=crimson, fontcolor=crimson];
119  S21 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
120  S21 -> S22 [ label = "e"];
121  S22 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
122  S22 -> S23 [ label = "r"];
123  S23 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
124  S23 -> S24 [ label = "v"];
125  S24 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
126  S24 -> S25 [ label = "i"];
127  S25 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
128  S25 -> S26 [ label = "c"];
129  S26 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
130  S26 -> S27 [ label = "e"];
131  S27 -> S28 [ label = ":"];
132  S27 -> S9 [ label = "<else,1>", color=crimson, fontcolor=crimson];
133  S28 -> S29 [ label = "/"];
134  S28 -> S9 [ label = "<else,2>", color=crimson, fontcolor=crimson];
135  S29 -> T_URL_SERVICE [ label = "/"];
136  S29 -> S9 [ label = "<else,3>", color=crimson, fontcolor=crimson];
137  S30 -> T_WILD_MULTI[ label = "*"];
138  S30 -> T_WILD_ONE [ label = "<else,1>", color=crimson, fontcolor=crimson];
139  S31 -> T_SEPARATOR [ label = "="];
140  S31 -> T_COLON [ label = "<else,1>", color=crimson, fontcolor=crimson];
141 }
142 */
143 
147 {
149  const unsigned char to_state;
151  const char range_start;
153  const char range_end;
155 
158 typedef struct rcl_lexer_state_s
159 {
161  const unsigned char else_state;
163  const unsigned char else_movement;
167 
168 #define S0 0u
169 #define S1 1u
170 #define S2 2u
171 #define S3 3u
172 #define S4 4u
173 #define S5 5u
174 #define S6 6u
175 #define S7 7u
176 #define S8 8u
177 #define S9 9u
178 #define S10 10u
179 #define S11 11u
180 #define S12 12u
181 #define S13 13u
182 #define S14 14u
183 #define S15 15u
184 #define S16 16u
185 #define S17 17u
186 #define S18 18u
187 #define S19 19u
188 #define S20 20u
189 #define S21 21u
190 #define S22 22u
191 #define S23 23u
192 #define S24 24u
193 #define S25 25u
194 #define S26 26u
195 #define S27 27u
196 #define S28 28u
197 #define S29 29u
198 #define S30 30u
199 #define S31 31u
200 #define LAST_STATE S31
201 
202 #define T_TILDE_SLASH 32u
203 #define T_URL_SERVICE 33u
204 #define T_URL_TOPIC 34u
205 #define T_COLON 35u
206 #define T_NODE 36u
207 #define T_NS 37u
208 #define T_SEPARATOR 38u
209 #define T_BR1 39u
210 #define T_BR2 40u
211 #define T_BR3 41u
212 #define T_BR4 42u
213 #define T_BR5 43u
214 #define T_BR6 44u
215 #define T_BR7 45u
216 #define T_BR8 46u
217 #define T_BR9 47u
218 #define T_TOKEN 48u
219 #define T_FORWARD_SLASH 49u
220 #define T_WILD_ONE 50u
221 #define T_WILD_MULTI 51u
222 #define T_EOF 52u
223 #define T_NONE 53u
224 #define T_DOT 54u
225 
226 // used to figure out if a state is terminal or not
227 #define FIRST_TERMINAL T_TILDE_SLASH
228 #define LAST_TERMINAL T_NONE
229 
230 // Used to mark where the last transition is in a state
231 #define END_TRANSITIONS {0, '\0', '\0'}
232 
233 static const rcl_lexer_state_t g_states[LAST_STATE + 1] =
234 {
235  // S0
236  {
237  T_NONE,
238  0u,
239  {
240  {T_FORWARD_SLASH, '/', '/'},
241  {T_DOT, '.', '.'},
242  {S1, '\\', '\\'},
243  {S2, '~', '~'},
244  {S3, '_', '_'},
245  {S9, 'a', 'q'},
246  {S9, 's', 'z'},
247  {S9, 'A', 'Z'},
248  {S11, 'r', 'r'},
249  {S30, '*', '*'},
250  {S31, ':', ':'},
251  END_TRANSITIONS
252  }
253  },
254  // S1
255  {
256  T_NONE,
257  0u,
258  {
259  {T_BR1, '1', '1'},
260  {T_BR2, '2', '2'},
261  {T_BR3, '3', '3'},
262  {T_BR4, '4', '4'},
263  {T_BR5, '5', '5'},
264  {T_BR6, '6', '6'},
265  {T_BR7, '7', '7'},
266  {T_BR8, '8', '8'},
267  {T_BR9, '9', '9'},
268  END_TRANSITIONS
269  }
270  },
271  // S2
272  {
273  T_NONE,
274  0u,
275  {
276  {T_TILDE_SLASH, '/', '/'},
277  END_TRANSITIONS
278  }
279  },
280  // S3
281  {
282  S10,
283  1u,
284  {
285  {S4, '_', '_'},
286  END_TRANSITIONS
287  }
288  },
289  // S4
290  {
291  T_NONE,
292  0u,
293  {
294  {S5, 'n', 'n'},
295  END_TRANSITIONS
296  }
297  },
298  // S5
299  {
300  T_NONE,
301  0u,
302  {
303  {T_NS, 's', 's'},
304  {S6, 'o', 'o'},
305  {S7, 'a', 'a'},
306  END_TRANSITIONS
307  }
308  },
309  // S6
310  {
311  T_NONE,
312  0u,
313  {
314  {S8, 'd', 'd'},
315  END_TRANSITIONS
316  }
317  },
318  // S7
319  {
320  T_NONE,
321  0u,
322  {
323  {S8, 'm', 'm'},
324  END_TRANSITIONS
325  }
326  },
327  // S8
328  {
329  T_NONE,
330  0u,
331  {
332  {T_NODE, 'e', 'e'},
333  END_TRANSITIONS
334  }
335  },
336  // S9
337  {
338  T_TOKEN,
339  1u,
340  {
341  {S9, 'a', 'z'},
342  {S9, 'A', 'Z'},
343  {S9, '0', '9'},
344  {S10, '_', '_'},
345  END_TRANSITIONS
346  }
347  },
348  // S10
349  {
350  T_TOKEN,
351  1u,
352  {
353  {S9, 'a', 'z'},
354  {S9, 'A', 'Z'},
355  {S9, '0', '9'},
356  END_TRANSITIONS
357  }
358  },
359  // S11
360  {
361  S9,
362  1u,
363  {
364  {S12, 'o', 'o'},
365  END_TRANSITIONS
366  }
367  },
368  // S12
369  {
370  S9,
371  1u,
372  {
373  {S13, 's', 's'},
374  END_TRANSITIONS
375  }
376  },
377  // S13
378  {
379  S9,
380  1u,
381  {
382  {S14, 't', 't'},
383  {S21, 's', 's'},
384  END_TRANSITIONS
385  }
386  },
387  // S14
388  {
389  S9,
390  1u,
391  {
392  {S15, 'o', 'o'},
393  END_TRANSITIONS
394  }
395  },
396  // S15
397  {
398  S9,
399  1u,
400  {
401  {S16, 'p', 'p'},
402  END_TRANSITIONS
403  }
404  },
405  // S16
406  {
407  S9,
408  1u,
409  {
410  {S17, 'i', 'i'},
411  END_TRANSITIONS
412  }
413  },
414  // S17
415  {
416  S9,
417  1u,
418  {
419  {S18, 'c', 'c'},
420  END_TRANSITIONS
421  }
422  },
423  // S18
424  {
425  S9,
426  1u,
427  {
428  {S19, ':', ':'},
429  END_TRANSITIONS
430  }
431  },
432  // S19
433  {
434  S9,
435  2u,
436  {
437  {S20, '/', '/'},
438  END_TRANSITIONS
439  }
440  },
441  // S20
442  {
443  S9,
444  3u,
445  {
446  {T_URL_TOPIC, '/', '/'},
447  END_TRANSITIONS
448  }
449  },
450  // S21
451  {
452  S9,
453  1u,
454  {
455  {S22, 'e', 'e'},
456  END_TRANSITIONS
457  }
458  },
459  // S21
460  {
461  S9,
462  1u,
463  {
464  {S23, 'r', 'r'},
465  END_TRANSITIONS
466  }
467  },
468  // S23
469  {
470  S9,
471  1u,
472  {
473  {S24, 'v', 'v'},
474  END_TRANSITIONS
475  }
476  },
477  // S24
478  {
479  S9,
480  1u,
481  {
482  {S25, 'i', 'i'},
483  END_TRANSITIONS
484  }
485  },
486  // S25
487  {
488  S9,
489  1u,
490  {
491  {S26, 'c', 'c'},
492  END_TRANSITIONS
493  }
494  },
495  // S26
496  {
497  S9,
498  1u,
499  {
500  {S27, 'e', 'e'},
501  END_TRANSITIONS
502  }
503  },
504  // S27
505  {
506  S9,
507  1u,
508  {
509  {S28, ':', ':'},
510  END_TRANSITIONS
511  }
512  },
513  // S28
514  {
515  S9,
516  2u,
517  {
518  {S29, '/', '/'},
519  END_TRANSITIONS
520  }
521  },
522  // S29
523  {
524  S9,
525  3u,
526  {
527  {T_URL_SERVICE, '/', '/'},
528  END_TRANSITIONS
529  }
530  },
531  // S30
532  {
533  T_WILD_ONE,
534  1u,
535  {
536  {T_WILD_MULTI, '*', '*'},
537  END_TRANSITIONS
538  }
539  },
540  // S31
541  {
542  T_COLON,
543  1u,
544  {
545  {T_SEPARATOR, '=', '='},
546  END_TRANSITIONS
547  }
548  }
549 };
550 
551 static const rcl_lexeme_t g_terminals[LAST_TERMINAL + 1] = {
552  // 0
554  // 1
556  // 2
558  // 3
560  // 4
562  // 5
564  // 6
566  // 7
568  // 8
570  // 9
572  // 10
574  // 11
576  // 12
578  // 13
580  // 14
582  // 15
584  // 16
586  // 17
588  // 18
590  // 19
592  // 20
594  // 21
596  // 22
598 };
599 
600 rcl_ret_t
602  const char * text,
603  rcl_lexeme_t * lexeme,
604  size_t * length)
605 {
606  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
607  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
608 
609  RCL_CHECK_ARGUMENT_FOR_NULL(text, RCL_RET_INVALID_ARGUMENT);
610  RCL_CHECK_ARGUMENT_FOR_NULL(lexeme, RCL_RET_INVALID_ARGUMENT);
611  RCL_CHECK_ARGUMENT_FOR_NULL(length, RCL_RET_INVALID_ARGUMENT);
612 
613  *length = 0u;
614 
615  if ('\0' == text[0u]) {
616  // Early exit if string is empty
617  *lexeme = RCL_LEXEME_EOF;
618  return RCL_RET_OK;
619  }
620 
621  const rcl_lexer_state_t * state;
622  char current_char;
623  size_t next_state = S0;
624  size_t movement;
625 
626  // Analyze one character at a time until lexeme is found
627  do {
628  if (next_state > LAST_STATE) {
629  // Should never happen
630  RCL_SET_ERROR_MSG("Internal lexer bug: next state does not exist");
631  return RCL_RET_ERROR;
632  }
633  state = &(g_states[next_state]);
634  current_char = text[*length];
635  next_state = 0u;
636  movement = 0u;
637 
638  // Look for a transition that contains this character in its range
639  size_t transition_idx = 0u;
640  const rcl_lexer_transition_t * transition;
641  do {
642  transition = &(state->transitions[transition_idx]);
643  if (transition->range_start <= current_char && transition->range_end >= current_char) {
644  next_state = transition->to_state;
645  break;
646  }
647  ++transition_idx;
648  } while (0u != transition->to_state);
649 
650  // if no transition was found, take the else transition
651  if (0u == next_state) {
652  next_state = state->else_state;
653  movement = state->else_movement;
654  }
655 
656  if (0u == movement) {
657  if ('\0' != current_char) {
658  // Go forwards 1 char as long as the end hasn't been reached
659  ++(*length);
660  }
661  } else {
662  // Go backwards N chars
663  if (movement - 1u > *length) {
664  // Should never happen
665  RCL_SET_ERROR_MSG("Internal lexer bug: movement would read before start of string");
666  return RCL_RET_ERROR;
667  }
668  *length -= movement - 1u;
669  }
670  } while (next_state < FIRST_TERMINAL);
671 
672  if (FIRST_TERMINAL > next_state || next_state - FIRST_TERMINAL > LAST_TERMINAL) {
673  // Should never happen
674  RCL_SET_ERROR_MSG("Internal lexer bug: terminal state does not exist");
675  return RCL_RET_ERROR;
676  }
677  *lexeme = g_terminals[next_state - FIRST_TERMINAL];
678  return RCL_RET_OK;
679 }
@ RCL_LEXEME_WILD_ONE
Definition: lexer.h:75
@ RCL_LEXEME_NS
__ns
Definition: lexer.h:49
@ RCL_LEXEME_TOKEN
a name between slashes, must match (([a-zA-Z](_)?)|_)([0-9a-zA-Z](_)?)*
Definition: lexer.h:71
@ RCL_LEXEME_NONE
Indicates no valid lexeme was found (end of input not reached)
Definition: lexer.h:35
@ RCL_LEXEME_EOF
Indicates end of input has been reached.
Definition: lexer.h:37
@ RCL_LEXEME_URL_TOPIC
rostopic://
Definition: lexer.h:43
@ RCL_LEXEME_BR2
\2
Definition: lexer.h:55
@ RCL_LEXEME_BR8
\8
Definition: lexer.h:67
@ RCL_LEXEME_BR4
\4
Definition: lexer.h:59
@ RCL_LEXEME_BR9
\9
Definition: lexer.h:69
@ RCL_LEXEME_URL_SERVICE
rosservice://
Definition: lexer.h:41
@ RCL_LEXEME_BR1
\1
Definition: lexer.h:53
@ RCL_LEXEME_BR6
\6
Definition: lexer.h:63
@ RCL_LEXEME_TILDE_SLASH
~/
Definition: lexer.h:39
@ RCL_LEXEME_FORWARD_SLASH
/
Definition: lexer.h:73
@ RCL_LEXEME_SEPARATOR
:=
Definition: lexer.h:51
@ RCL_LEXEME_BR3
\3
Definition: lexer.h:57
@ RCL_LEXEME_BR5
\5
Definition: lexer.h:61
@ RCL_LEXEME_BR7
\7
Definition: lexer.h:65
@ RCL_LEXEME_NODE
__node or __name
Definition: lexer.h:47
@ RCL_LEXEME_DOT
.
Definition: lexer.h:81
@ RCL_LEXEME_WILD_MULTI
**
Definition: lexer.h:77
@ RCL_LEXEME_COLON
:
Definition: lexer.h:45
enum rcl_lexeme_e rcl_lexeme_t
Type of lexeme found by lexical analysis.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_lexer_analyze(const char *text, rcl_lexeme_t *lexeme, size_t *length)
Do lexical analysis on a string.
Definition: lexer.c:601
const unsigned char else_movement
Movement associated with taking else state.
Definition: lexer.c:163
const unsigned char else_state
Transition to this state if no other transition matches.
Definition: lexer.c:161
const rcl_lexer_transition_t transitions[12]
Transitions in the state machine (NULL value at end of array)
Definition: lexer.c:165
const unsigned char to_state
Index of a state to transition to.
Definition: lexer.c:149
const char range_end
End of a range of chars (inclusive) which activates this transition.
Definition: lexer.c:153
const char range_start
Start of a range of chars (inclusive) which activates this transition.
Definition: lexer.c:151
#define RCL_RET_OK
Success return code.
Definition: types.h:28
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:36
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:30
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:25