Static remapping with url scheme (#227)
* DIRTY lexer with bugs rosservice: and rostopic: * Fix bug with partial url schemes * Style fixes * Moved lexer.h to satisfy cpplint * moved terminals to their own array to reduce code size * Shrink lexer by using char, add error checking * Comment/whitespace * comment * terminal -> lexeme where appropriate * Static const global * Documentation and argument order * Add rcl_lexer_lookahead2_t * Allow ignoring lexeme text * Beginnings of recursive descent parser * Add method to get current position in text * new remap parsing passes unit tests * Test rosservice:// and rostopic:// * fix movement formula comment * doxygent comment * move code to make pr diff easier to read * Comments * Comment * Comment about impossibilities * Set error message * unsigned literals * Add a couple more url scheme tests * remove out of date comment * end_pos -> length * another token text * Whitespace * call accept -> accept * use array instead of number suffix * Missing return; wrong comment * test methods at end of input * Test not zero-initialized init * Test lexing remapping rules * Windows warning * Remove const to avoid discarding during cast
This commit is contained in:
parent
c51f8925f4
commit
ff024ee9fd
12 changed files with 2532 additions and 163 deletions
|
@ -33,6 +33,8 @@ set(${PROJECT_NAME}_sources
|
|||
src/rcl/expand_topic_name.c
|
||||
src/rcl/graph.c
|
||||
src/rcl/guard_condition.c
|
||||
src/rcl/lexer.c
|
||||
src/rcl/lexer_lookahead.c
|
||||
src/rcl/node.c
|
||||
src/rcl/publisher.c
|
||||
src/rcl/rcl.c
|
||||
|
|
118
rcl/include/rcl/lexer.h
Normal file
118
rcl/include/rcl/lexer.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
// Copyright 2018 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCL__LEXER_H_
|
||||
#define RCL__LEXER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "rcl/allocator.h"
|
||||
#include "rcl/macros.h"
|
||||
#include "rcl/types.h"
|
||||
#include "rcl/visibility_control.h"
|
||||
|
||||
#if __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/// Type of lexeme found by lexical analysis.
|
||||
typedef enum rcl_lexeme_t
|
||||
{
|
||||
/// Indicates no valid lexeme was found
|
||||
RCL_LEXEME_NONE = 0,
|
||||
/// Indicates end of input has been reached
|
||||
RCL_LEXEME_EOF = 1,
|
||||
/// ~/
|
||||
RCL_LEXEME_TILDE_SLASH = 2,
|
||||
/// rosservice://
|
||||
RCL_LEXEME_URL_SERVICE = 3,
|
||||
/// rostopic://
|
||||
RCL_LEXEME_URL_TOPIC = 4,
|
||||
/// :
|
||||
RCL_LEXEME_COLON = 5,
|
||||
/// __node
|
||||
RCL_LEXEME_NODE = 6,
|
||||
/// __ns
|
||||
RCL_LEXEME_NS = 7,
|
||||
/// :=
|
||||
RCL_LEXEME_SEPARATOR = 8,
|
||||
/// \1
|
||||
RCL_LEXEME_BR1 = 9,
|
||||
/// \2
|
||||
RCL_LEXEME_BR2 = 10,
|
||||
/// \3
|
||||
RCL_LEXEME_BR3 = 11,
|
||||
/// \4
|
||||
RCL_LEXEME_BR4 = 12,
|
||||
/// \5
|
||||
RCL_LEXEME_BR5 = 13,
|
||||
/// \6
|
||||
RCL_LEXEME_BR6 = 14,
|
||||
/// \7
|
||||
RCL_LEXEME_BR7 = 15,
|
||||
/// \8
|
||||
RCL_LEXEME_BR8 = 16,
|
||||
/// \9
|
||||
RCL_LEXEME_BR9 = 17,
|
||||
/// a name between slashes, must match (([a-zA-Z](_)?)|_)([0-9a-zA-Z](_)?)*
|
||||
RCL_LEXEME_TOKEN = 18,
|
||||
/// /
|
||||
RCL_LEXEME_FORWARD_SLASH = 19,
|
||||
/// *
|
||||
RCL_LEXEME_WILD_ONE = 20,
|
||||
/// **
|
||||
RCL_LEXEME_WILD_MULTI = 21
|
||||
} rcl_lexeme_t;
|
||||
|
||||
|
||||
/// Do lexical analysis on a string.
|
||||
/**
|
||||
* This function analyzes a string to see if it starts with a valid lexeme.
|
||||
* If the string does not begin with a valid lexeme then lexeme will be RCL_LEXEME_NONE, and the
|
||||
* length will be set to include the character that made it impossible.
|
||||
* If the first character is '\0' then lexeme will be RCL_LEXEME_EOF.
|
||||
*
|
||||
* <hr>
|
||||
* Attribute | Adherence
|
||||
* ------------------ | -------------
|
||||
* Allocates Memory | Yes [1]
|
||||
* Thread-Safe | Yes
|
||||
* Uses Atomics | No
|
||||
* Lock-Free | Yes
|
||||
* <i>[1] Only allocates if an argument is invalid or an internal bug is detected.</i>
|
||||
*
|
||||
* \param[in] text The string to analyze.
|
||||
* \param[in] allocator An allocator to use if an error occurs.
|
||||
* \param[out] lexeme The type of lexeme found in the string.
|
||||
* \param[out] length The length of text in the string that constitutes the found lexeme.
|
||||
* \return `RCL_RET_OK` if analysis is successful regardless whether a valid lexeme is found, or
|
||||
* \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or
|
||||
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
|
||||
* \return `RCL_RET_ERROR` if an internal bug is detected.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_lexer_analyze(
|
||||
const char * text,
|
||||
rcl_allocator_t allocator,
|
||||
rcl_lexeme_t * lexeme,
|
||||
size_t * length);
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RCL__LEXER_H_
|
261
rcl/include/rcl/lexer_lookahead.h
Normal file
261
rcl/include/rcl/lexer_lookahead.h
Normal file
|
@ -0,0 +1,261 @@
|
|||
// Copyright 2018 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCL__LEXER_LOOKAHEAD_H_
|
||||
#define RCL__LEXER_LOOKAHEAD_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "rcl/allocator.h"
|
||||
#include "rcl/lexer.h"
|
||||
#include "rcl/macros.h"
|
||||
#include "rcl/types.h"
|
||||
#include "rcl/visibility_control.h"
|
||||
|
||||
#if __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// Forward declaration
|
||||
struct rcl_lexer_lookahead2_impl_t;
|
||||
|
||||
/// Track lexical analysis and allow looking ahead 2 lexemes.
|
||||
typedef struct rcl_lexer_lookahead2_t
|
||||
{
|
||||
struct rcl_lexer_lookahead2_impl_t * impl;
|
||||
} rcl_lexer_lookahead2_t;
|
||||
|
||||
/// Get a zero initialized rcl_lexer_lookahead2_t instance.
|
||||
/**
|
||||
* \sa rcl_lexer_lookahead2_init()
|
||||
* <hr>
|
||||
* Attribute | Adherence
|
||||
* ------------------ | -------------
|
||||
* Allocates Memory | No
|
||||
* Thread-Safe | Yes
|
||||
* Uses Atomics | No
|
||||
* Lock-Free | Yes
|
||||
*
|
||||
* \return zero initialized lookahead2 buffer.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_lexer_lookahead2_t
|
||||
rcl_get_zero_initialized_lexer_lookahead2();
|
||||
|
||||
/// Initialize an rcl_lexer_lookahead2_t instance.
|
||||
/**
|
||||
* The lookahead2 buffer borrows a reference to the provided text.
|
||||
* The text must not be freed before the buffer is finalized.
|
||||
* The lookahead2 buffer only needs to be finalized if this function does not return RCL_RET_OK.
|
||||
* \sa rcl_lexer_lookahead2_fini()
|
||||
*
|
||||
* <hr>
|
||||
* Attribute | Adherence
|
||||
* ------------------ | -------------
|
||||
* Allocates Memory | Yes
|
||||
* Thread-Safe | No
|
||||
* Uses Atomics | No
|
||||
* Lock-Free | Yes
|
||||
*
|
||||
* \param[in] buffer A buffer that is zero initialized.
|
||||
* \sa rcl_get_zero_initialized_lexer_lookahead2()
|
||||
* \param[in] text The string to analyze.
|
||||
* \param[in] allocator An allocator to use if an error occurs.
|
||||
* \return `RCL_RET_OK` if the buffer is successfully initialized, or
|
||||
* \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or
|
||||
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
|
||||
* \return `RCL_RET_ERROR` if an unspecified error occurrs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_init(
|
||||
rcl_lexer_lookahead2_t * buffer,
|
||||
const char * text,
|
||||
rcl_allocator_t allocator);
|
||||
|
||||
/// Finalize an instance of an rcl_lexer_lookahead2_t structure.
|
||||
/**
|
||||
* \sa rcl_lexer_lookahead2_init()
|
||||
*
|
||||
* <hr>
|
||||
* Attribute | Adherence
|
||||
* ------------------ | -------------
|
||||
* Allocates Memory | Yes [1]
|
||||
* Thread-Safe | No
|
||||
* Uses Atomics | No
|
||||
* Lock-Free | Yes
|
||||
* <i>[1] Only allocates if an argument is invalid.</i>
|
||||
*
|
||||
* \param[in] buffer The structure to be deallocated.
|
||||
* \return `RCL_RET_OK` if the structure was successfully finalized, or
|
||||
* \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or
|
||||
* \return `RCL_RET_ERROR` if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_fini(
|
||||
rcl_lexer_lookahead2_t * buffer);
|
||||
|
||||
/// Look ahead at the next lexeme in the string.
|
||||
/**
|
||||
* Repeated calls to peek will return the same lexeme.
|
||||
* A parser that deems the next lexeme as valid must accept it to advance lexing.
|
||||
* \sa rcl_lexer_lookahead2_accept()
|
||||
*
|
||||
* <hr>
|
||||
* Attribute | Adherence
|
||||
* ------------------ | -------------
|
||||
* Allocates Memory | Yes [1]
|
||||
* Thread-Safe | No
|
||||
* Uses Atomics | No
|
||||
* Lock-Free | Yes
|
||||
* <i>[1] Only allocates if an argument is invalid or an internal bug is detected.</i>
|
||||
*
|
||||
* \param[in] buffer the lookahead2 buffer being used to analyze a string.
|
||||
* \param[out] next_type an output variable for the next lexeme in the string.
|
||||
* \return `RCL_RET_OK` if peeking was successfull, or
|
||||
* \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or
|
||||
* \return `RCL_RET_ERROR` if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_peek(
|
||||
rcl_lexer_lookahead2_t * buffer,
|
||||
rcl_lexeme_t * next_type);
|
||||
|
||||
/// Look ahead at the next two lexemes in the string.
|
||||
/**
|
||||
* Repeated calls to peek2 will return the same two lexemes.
|
||||
* A parser that deems the next two lexemes as valid must accept twice to advance lexing.
|
||||
* \sa rcl_lexer_lookahead2_accept()
|
||||
*
|
||||
* <hr>
|
||||
* Attribute | Adherence
|
||||
* ------------------ | -------------
|
||||
* Allocates Memory | Yes [1]
|
||||
* Thread-Safe | No
|
||||
* Uses Atomics | No
|
||||
* Lock-Free | Yes
|
||||
* <i>[1] Only allocates if an argument is invalid or an internal bug is detected.</i>
|
||||
*
|
||||
* \param[in] buffer the lookahead2 buffer being used to analyze a string.
|
||||
* \param[out] next_type1 an output variable for the next lexeme in the string.
|
||||
* \param[out] next_type2 an output variable for the lexeme after the next lexeme in the string.
|
||||
* \return `RCL_RET_OK` if peeking was successfull, or
|
||||
* \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or
|
||||
* \return `RCL_RET_ERROR` if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_peek2(
|
||||
rcl_lexer_lookahead2_t * buffer,
|
||||
rcl_lexeme_t * next_type1,
|
||||
rcl_lexeme_t * next_type2);
|
||||
|
||||
/// Accept a lexeme and advance analysis.
|
||||
/**
|
||||
* A token must have been peeked before it can be accepted.
|
||||
* \sa rcl_lexer_lookahead2_peek()
|
||||
* \sa rcl_lexer_lookahead2_peek2()
|
||||
*
|
||||
* <hr>
|
||||
* Attribute | Adherence
|
||||
* ------------------ | -------------
|
||||
* Allocates Memory | Yes [1]
|
||||
* Thread-Safe | No
|
||||
* Uses Atomics | No
|
||||
* Lock-Free | Yes
|
||||
* <i>[1] Only allocates if an argument is invalid or an error occurs.</i>
|
||||
*
|
||||
* \param[in] buffer the lookahead2 buffer being used to analyze a string.
|
||||
* \param[out] lexeme_text pointer to where lexeme begins in string.
|
||||
* \param[out] lexeme_text_length length of lexeme_text.
|
||||
* \return `RCL_RET_OK` if peeking was successfull, or
|
||||
* \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or
|
||||
* \return `RCL_RET_ERROR` if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_accept(
|
||||
rcl_lexer_lookahead2_t * buffer,
|
||||
const char ** lexeme_text,
|
||||
size_t * lexeme_text_length);
|
||||
|
||||
/// Require the next lexeme to be a certain type and advance analysis.
|
||||
/**
|
||||
* This method is a shortcut to peeking and accepting a lexeme.
|
||||
* It should be used by a parser when there is only one valid lexeme that could come next.
|
||||
* \sa rcl_lexer_lookahead2_peek()
|
||||
* \sa rcl_lexer_lookahead2_accept()
|
||||
*
|
||||
* <hr>
|
||||
* Attribute | Adherence
|
||||
* ------------------ | -------------
|
||||
* Allocates Memory | Yes [1]
|
||||
* Thread-Safe | No
|
||||
* Uses Atomics | No
|
||||
* Lock-Free | Yes
|
||||
* <i>[1] Only allocates if an argument is invalid or an error occurs.</i>
|
||||
*
|
||||
* \param[in] buffer the lookahead2 buffer being used to analyze a string.
|
||||
* \param[in] type the type the next lexeme must be.
|
||||
* \param[out] lexeme_text pointer to where lexeme begins in string.
|
||||
* \param[out] lexeme_text_length length of lexeme_text.
|
||||
* \return `RCL_RET_OK` if the next lexeme was the expected one, or
|
||||
* \return `RCL_RET_WRONG_LEXEME` if the next lexeme was not the expected one, or
|
||||
* \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or
|
||||
* \return `RCL_RET_ERROR` if an unspecified error occurs.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_expect(
|
||||
rcl_lexer_lookahead2_t * buffer,
|
||||
rcl_lexeme_t type,
|
||||
const char ** lexeme_text,
|
||||
size_t * lexeme_text_length);
|
||||
|
||||
/// Get the text at the point where it is currently being analyzed.
|
||||
/**
|
||||
* <hr>
|
||||
* Attribute | Adherence
|
||||
* ------------------ | -------------
|
||||
* Allocates Memory | No
|
||||
* Thread-Safe | Yes
|
||||
* Uses Atomics | No
|
||||
* Lock-Free | Yes
|
||||
*
|
||||
* \param[in] buffer the lookahead2 buffer being used to analyze a string.
|
||||
* \return a pointer inside the original text at the position being analyzed, or
|
||||
* \return `NULL` if buffer is itself `NULL` or zero initialized, or
|
||||
* \return an undefined value if buffer is not initialized or has been finalized.
|
||||
*/
|
||||
RCL_PUBLIC
|
||||
RCL_WARN_UNUSED
|
||||
const char *
|
||||
rcl_lexer_lookahead2_get_text(
|
||||
const rcl_lexer_lookahead2_t * buffer);
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RCL__LEXER_LOOKAHEAD_H_
|
|
@ -90,5 +90,7 @@ typedef rmw_ret_t rcl_ret_t;
|
|||
// rcl argument parsing specific ret codes in 1XXX
|
||||
/// Argument is not a valid remap rule
|
||||
#define RCL_RET_INVALID_REMAP_RULE 1001
|
||||
/// Expected one type of lexeme but got another
|
||||
#define RCL_RET_WRONG_LEXEME 1002
|
||||
|
||||
#endif // RCL__TYPES_H_
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "./arguments_impl.h"
|
||||
#include "./remap_impl.h"
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/lexer_lookahead.h"
|
||||
#include "rcl/validate_topic_name.h"
|
||||
#include "rcutils/allocator.h"
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
@ -48,169 +49,7 @@ rcl_ret_t
|
|||
_rcl_parse_remap_rule(
|
||||
const char * arg,
|
||||
rcl_allocator_t allocator,
|
||||
rcl_remap_t * output_rule)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT, allocator);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(output_rule, RCL_RET_INVALID_ARGUMENT, allocator);
|
||||
|
||||
size_t len_node_name = 0;
|
||||
size_t len_match = 0;
|
||||
size_t len_replacement = 0;
|
||||
|
||||
const char * separator = NULL;
|
||||
const char * colon = NULL;
|
||||
const char * match_begin = arg;
|
||||
const char * replacement_begin = NULL;
|
||||
|
||||
// A valid rule has two parts separated by :=
|
||||
separator = strstr(arg, ":=");
|
||||
if (NULL == separator) {
|
||||
RCL_SET_ERROR_MSG("missing :=", allocator);
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
|
||||
replacement_begin = separator + 2;
|
||||
|
||||
// must have characters on both sides of the separator
|
||||
len_match = separator - arg;
|
||||
len_replacement = strlen(replacement_begin);
|
||||
if (0 == len_match) {
|
||||
RCL_SET_ERROR_MSG("match is zero length", allocator);
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
} else if (0 == len_replacement) {
|
||||
RCL_SET_ERROR_MSG("replacement has zero length", allocator);
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
|
||||
colon = strchr(arg, ':');
|
||||
if (NULL != colon) {
|
||||
if (colon < separator) {
|
||||
// If there is a : on the match side then there is a node-name prefix
|
||||
match_begin = colon + 1;
|
||||
len_node_name = colon - arg;
|
||||
len_match = separator - match_begin;
|
||||
// node name must have at least one character
|
||||
if (len_node_name <= 0) {
|
||||
RCL_SET_ERROR_MSG("node name previx has zero length", allocator);
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
} else if (colon > separator) {
|
||||
// If the colon is on the replacement side then this couldn't be a valid rule
|
||||
RCL_SET_ERROR_MSG("replacement side cannot contain a :", allocator);
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe match length changed because there was a node name prefix
|
||||
if (0 == len_match) {
|
||||
RCL_SET_ERROR_MSG("match is zero length", allocator);
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
|
||||
// Make sure node name contains only valid characters
|
||||
if (len_node_name) {
|
||||
int validation_result;
|
||||
size_t invalid_index;
|
||||
rmw_ret_t rmw_ret = rmw_validate_node_name_with_size(
|
||||
arg, len_node_name, &validation_result, &invalid_index);
|
||||
if (RMW_RET_OK != rmw_ret) {
|
||||
RCL_SET_ERROR_MSG("failed to run check on node name", allocator);
|
||||
return RCL_RET_ERROR;
|
||||
} else if (RMW_NODE_NAME_VALID != validation_result) {
|
||||
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
|
||||
allocator,
|
||||
"node name prefix invalid: %s", rmw_node_name_validation_result_string(validation_result));
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out what type of rule this is, default is to apply to topic and service names
|
||||
rcl_remap_type_t type = RCL_TOPIC_REMAP | RCL_SERVICE_REMAP;
|
||||
if (4 == len_match && 0 == strncmp("__ns", match_begin, len_match)) {
|
||||
type = RCL_NAMESPACE_REMAP;
|
||||
} else if (6 == len_match && 0 == strncmp("__node", match_begin, len_match)) {
|
||||
type = RCL_NODENAME_REMAP;
|
||||
}
|
||||
|
||||
if (type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
|
||||
// Replacement must be a valid topic name
|
||||
int validation_result;
|
||||
size_t invalid_index;
|
||||
rcl_ret_t ret = rcl_validate_topic_name(replacement_begin, &validation_result, &invalid_index);
|
||||
if (ret != RCL_RET_OK) {
|
||||
return RCL_RET_ERROR;
|
||||
} else if (validation_result != RCL_TOPIC_NAME_VALID) {
|
||||
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
|
||||
allocator,
|
||||
"replacement is invalid: %s", rcl_topic_name_validation_result_string(validation_result));
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
// Match must be a valid topic name
|
||||
ret = rcl_validate_topic_name_with_size(
|
||||
match_begin, len_match, &validation_result, &invalid_index);
|
||||
if (ret != RCL_RET_OK) {
|
||||
return RCL_RET_ERROR;
|
||||
} else if (validation_result != RCL_TOPIC_NAME_VALID) {
|
||||
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
|
||||
allocator,
|
||||
"match is invalid: %s", rcl_topic_name_validation_result_string(validation_result));
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
} else if (RCL_NAMESPACE_REMAP == type) {
|
||||
int validation_result;
|
||||
size_t invalid_idx;
|
||||
rmw_ret_t rmw_ret = rmw_validate_namespace(replacement_begin, &validation_result, &invalid_idx);
|
||||
if (RMW_RET_OK != rmw_ret) {
|
||||
RCL_SET_ERROR_MSG("failed to run check on namespace", allocator);
|
||||
return RCL_RET_ERROR;
|
||||
} else if (RMW_NAMESPACE_VALID != validation_result) {
|
||||
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
|
||||
allocator,
|
||||
"namespace is invalid: %s", rmw_namespace_validation_result_string(validation_result));
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
} else if (RCL_NODENAME_REMAP == type) {
|
||||
int validation_result;
|
||||
size_t invalid_idx;
|
||||
rmw_ret_t rmw_ret = rmw_validate_node_name(replacement_begin, &validation_result, &invalid_idx);
|
||||
if (RMW_RET_OK != rmw_ret) {
|
||||
RCL_SET_ERROR_MSG("failed to run check on node name", allocator);
|
||||
return RCL_RET_ERROR;
|
||||
} else if (RMW_NODE_NAME_VALID != validation_result) {
|
||||
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
|
||||
allocator,
|
||||
"node name is invalid: %s", rmw_node_name_validation_result_string(validation_result));
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
}
|
||||
|
||||
// Rule is valid, construct a structure for it
|
||||
output_rule->allocator = allocator;
|
||||
output_rule->type = type;
|
||||
if (len_node_name > 0) {
|
||||
output_rule->node_name = rcutils_strndup(arg, len_node_name, allocator);
|
||||
if (NULL == output_rule->node_name) {
|
||||
goto cleanup_rule;
|
||||
}
|
||||
}
|
||||
if (type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
|
||||
output_rule->match = rcutils_strndup(match_begin, len_match, allocator);
|
||||
if (NULL == output_rule->match) {
|
||||
goto cleanup_rule;
|
||||
}
|
||||
}
|
||||
output_rule->replacement = rcutils_strndup(replacement_begin, len_replacement, allocator);
|
||||
if (NULL == output_rule->replacement) {
|
||||
goto cleanup_rule;
|
||||
}
|
||||
return RCL_RET_OK;
|
||||
|
||||
cleanup_rule:
|
||||
if (RCL_RET_OK != rcl_remap_fini(output_rule)) {
|
||||
RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME, "Failed to fini remap rule after error occurred");
|
||||
}
|
||||
return RCL_RET_BAD_ALLOC;
|
||||
}
|
||||
rcl_remap_t * output_rule);
|
||||
|
||||
rcl_ret_t
|
||||
rcl_parse_arguments(
|
||||
|
@ -447,6 +286,497 @@ rcl_get_global_arguments()
|
|||
return &__rcl_global_arguments;
|
||||
}
|
||||
|
||||
/// Parses a fully qualified namespace for a namespace replacement rule (ex: `/foo/bar`)
|
||||
/// \sa _rcl_parse_remap_begin_remap_rule()
|
||||
/// \internal
|
||||
RCL_LOCAL
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_fully_qualified_namespace(
|
||||
rcl_lexer_lookahead2_t * lex_lookahead)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
|
||||
// Must have at least one Forward slash /
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_FORWARD_SLASH, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
|
||||
// repeated tokens and slashes (allow trailing slash, but don't require it)
|
||||
while (true) {
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_TOKEN, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
rcl_reset_error();
|
||||
break;
|
||||
}
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_FORWARD_SLASH, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
rcl_reset_error();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
/// Parse either a token or a backreference (ex: `bar`, or `\7`).
|
||||
/// \sa _rcl_parse_remap_begin_remap_rule()
|
||||
/// \internal
|
||||
RCL_LOCAL
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_replacement_token(
|
||||
rcl_lexer_lookahead2_t * lex_lookahead,
|
||||
rcl_remap_t * rule)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexeme_t lexeme;
|
||||
|
||||
ret = rcl_lexer_lookahead2_peek(lex_lookahead, &lexeme);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (
|
||||
RCL_LEXEME_BR1 == lexeme || RCL_LEXEME_BR2 == lexeme || RCL_LEXEME_BR3 == lexeme ||
|
||||
RCL_LEXEME_BR4 == lexeme || RCL_LEXEME_BR5 == lexeme || RCL_LEXEME_BR6 == lexeme ||
|
||||
RCL_LEXEME_BR7 == lexeme || RCL_LEXEME_BR8 == lexeme || RCL_LEXEME_BR9 == lexeme)
|
||||
{
|
||||
RCL_SET_ERROR_MSG("Backreferences are not implemented", rule->allocator);
|
||||
return RCL_RET_ERROR;
|
||||
} else if (RCL_LEXEME_TOKEN == lexeme) {
|
||||
ret = rcl_lexer_lookahead2_accept(lex_lookahead, NULL, NULL);
|
||||
} else {
|
||||
ret = RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Parse the replacement side of a name remapping rule (ex: `bar/\1/foo`).
|
||||
/// \sa _rcl_parse_remap_begin_remap_rule()
|
||||
/// \internal
|
||||
RCL_LOCAL
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_replacement_name(
|
||||
rcl_lexer_lookahead2_t * lex_lookahead,
|
||||
rcl_remap_t * rule)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexeme_t lexeme;
|
||||
|
||||
const char * replacement_start = rcl_lexer_lookahead2_get_text(lex_lookahead);
|
||||
if (NULL == replacement_start) {
|
||||
RCL_SET_ERROR_MSG("failed to get start of replacement", rule->allocator);
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
|
||||
// private name (~/...) or fully qualified name (/...) ?
|
||||
ret = rcl_lexer_lookahead2_peek(lex_lookahead, &lexeme);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
if (RCL_LEXEME_TILDE_SLASH == lexeme || RCL_LEXEME_FORWARD_SLASH == lexeme) {
|
||||
ret = rcl_lexer_lookahead2_accept(lex_lookahead, NULL, NULL);
|
||||
}
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// token ( '/' token )*
|
||||
ret = _rcl_parse_remap_replacement_token(lex_lookahead, rule);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = rcl_lexer_lookahead2_peek(lex_lookahead, &lexeme);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
while (RCL_LEXEME_EOF != lexeme) {
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_FORWARD_SLASH, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
ret = _rcl_parse_remap_replacement_token(lex_lookahead, rule);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = rcl_lexer_lookahead2_peek(lex_lookahead, &lexeme);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy replacement into rule
|
||||
const char * replacement_end = rcl_lexer_lookahead2_get_text(lex_lookahead);
|
||||
size_t length = (size_t)(replacement_end - replacement_start);
|
||||
rule->replacement = rcutils_strndup(replacement_start, length, rule->allocator);
|
||||
if (NULL == rule->replacement) {
|
||||
RCL_SET_ERROR_MSG("failed to copy replacement", rule->allocator);
|
||||
return RCL_RET_BAD_ALLOC;
|
||||
}
|
||||
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
/// Parse either a token or a wildcard (ex: `foobar`, or `*`, or `**`).
|
||||
/// \sa _rcl_parse_remap_begin_remap_rule()
|
||||
/// \internal
|
||||
RCL_LOCAL
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_match_token(
|
||||
rcl_lexer_lookahead2_t * lex_lookahead,
|
||||
rcl_remap_t * rule)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexeme_t lexeme;
|
||||
|
||||
ret = rcl_lexer_lookahead2_peek(lex_lookahead, &lexeme);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (RCL_LEXEME_TOKEN == lexeme) {
|
||||
ret = rcl_lexer_lookahead2_accept(lex_lookahead, NULL, NULL);
|
||||
} else if (RCL_LEXEME_WILD_ONE == lexeme) {
|
||||
RCL_SET_ERROR_MSG("Wildcard '*' is not implemented", rule->allocator);
|
||||
return RCL_RET_ERROR;
|
||||
} else if (RCL_LEXEME_WILD_MULTI == lexeme) {
|
||||
RCL_SET_ERROR_MSG("Wildcard '**' is not implemented", rule->allocator);
|
||||
return RCL_RET_ERROR;
|
||||
} else {
|
||||
RCL_SET_ERROR_MSG("Expecting token or wildcard", rule->allocator);
|
||||
ret = RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Parse the match side of a name remapping rule (ex: `rostopic://foo`)
|
||||
/// \sa _rcl_parse_remap_begin_remap_rule()
|
||||
/// \internal
|
||||
RCL_LOCAL
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_match_name(
|
||||
rcl_lexer_lookahead2_t * lex_lookahead,
|
||||
rcl_remap_t * rule)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexeme_t lexeme;
|
||||
// rostopic:// rosservice://
|
||||
ret = rcl_lexer_lookahead2_peek(lex_lookahead, &lexeme);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
if (RCL_LEXEME_URL_SERVICE == lexeme) {
|
||||
rule->type = RCL_SERVICE_REMAP;
|
||||
ret = rcl_lexer_lookahead2_accept(lex_lookahead, NULL, NULL);
|
||||
} else if (RCL_LEXEME_URL_TOPIC == lexeme) {
|
||||
rule->type = RCL_TOPIC_REMAP;
|
||||
ret = rcl_lexer_lookahead2_accept(lex_lookahead, NULL, NULL);
|
||||
} else {
|
||||
rule->type = (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP);
|
||||
}
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char * match_start = rcl_lexer_lookahead2_get_text(lex_lookahead);
|
||||
if (NULL == match_start) {
|
||||
RCL_SET_ERROR_MSG("failed to get start of match", rule->allocator);
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
|
||||
// private name (~/...) or fully qualified name (/...) ?
|
||||
ret = rcl_lexer_lookahead2_peek(lex_lookahead, &lexeme);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
if (RCL_LEXEME_TILDE_SLASH == lexeme || RCL_LEXEME_FORWARD_SLASH == lexeme) {
|
||||
ret = rcl_lexer_lookahead2_accept(lex_lookahead, NULL, NULL);
|
||||
}
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// token ( '/' token )*
|
||||
ret = _rcl_parse_remap_match_token(lex_lookahead, rule);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = rcl_lexer_lookahead2_peek(lex_lookahead, &lexeme);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
while (RCL_LEXEME_SEPARATOR != lexeme) {
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_FORWARD_SLASH, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
ret = _rcl_parse_remap_match_token(lex_lookahead, rule);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = rcl_lexer_lookahead2_peek(lex_lookahead, &lexeme);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy match into rule
|
||||
const char * match_end = rcl_lexer_lookahead2_get_text(lex_lookahead);
|
||||
size_t length = (size_t)(match_end - match_start);
|
||||
rule->match = rcutils_strndup(match_start, length, rule->allocator);
|
||||
if (NULL == rule->match) {
|
||||
RCL_SET_ERROR_MSG("failed to copy match", rule->allocator);
|
||||
return RCL_RET_BAD_ALLOC;
|
||||
}
|
||||
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
/// Parse a name remapping rule (ex: `rostopic:///foo:=bar`).
|
||||
/// \sa _rcl_parse_remap_begin_remap_rule()
|
||||
/// \internal
|
||||
RCL_LOCAL
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_name_remap(
|
||||
rcl_lexer_lookahead2_t * lex_lookahead,
|
||||
rcl_remap_t * rule)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
// match
|
||||
ret = _rcl_parse_remap_match_name(lex_lookahead, rule);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
// :=
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_SEPARATOR, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
// replacement
|
||||
ret = _rcl_parse_remap_replacement_name(lex_lookahead, rule);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
/// Parse a namespace replacement rule (ex: `__ns:=/new/ns`).
|
||||
/// \sa _rcl_parse_remap_begin_remap_rule()
|
||||
/// \internal
|
||||
RCL_LOCAL
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_namespace_replacement(
|
||||
rcl_lexer_lookahead2_t * lex_lookahead,
|
||||
rcl_remap_t * rule)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
// __ns
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_NS, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
// :=
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_SEPARATOR, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
// /foo/bar
|
||||
const char * ns_start = rcl_lexer_lookahead2_get_text(lex_lookahead);
|
||||
if (NULL == ns_start) {
|
||||
RCL_SET_ERROR_MSG("failed to get start of namespace", rule->allocator);
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
ret = _rcl_parse_remap_fully_qualified_namespace(lex_lookahead);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Copy namespace into rule
|
||||
const char * ns_end = rcl_lexer_lookahead2_get_text(lex_lookahead);
|
||||
size_t length = (size_t)(ns_end - ns_start);
|
||||
rule->replacement = rcutils_strndup(ns_start, length, rule->allocator);
|
||||
if (NULL == rule->replacement) {
|
||||
RCL_SET_ERROR_MSG("failed to copy namespace", rule->allocator);
|
||||
return RCL_RET_BAD_ALLOC;
|
||||
}
|
||||
|
||||
rule->type = RCL_NAMESPACE_REMAP;
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
/// Parse a nodename replacement rule (ex: `__node:=new_name`).
|
||||
/// \sa _rcl_parse_remap_begin_remap_rule()
|
||||
/// \internal
|
||||
RCL_LOCAL
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_nodename_replacement(
|
||||
rcl_lexer_lookahead2_t * lex_lookahead,
|
||||
rcl_remap_t * rule)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
const char * node_name;
|
||||
size_t length;
|
||||
|
||||
// __node
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_NODE, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
// :=
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_SEPARATOR, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
// new_node_name
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_TOKEN, &node_name, &length);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
// copy the node name into the replacement side of the rule
|
||||
rule->replacement = rcutils_strndup(node_name, length, rule->allocator);
|
||||
if (NULL == rule->replacement) {
|
||||
RCL_SET_ERROR_MSG("failed to allocate node name", rule->allocator);
|
||||
return RCL_RET_BAD_ALLOC;
|
||||
}
|
||||
|
||||
rule->type = RCL_NODENAME_REMAP;
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
/// Parse a nodename prefix including trailing colon (ex: `node_name:`).
|
||||
/// \sa _rcl_parse_remap_begin_remap_rule()
|
||||
/// \internal
|
||||
RCL_LOCAL
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_nodename_prefix(
|
||||
rcl_lexer_lookahead2_t * lex_lookahead,
|
||||
rcl_remap_t * rule)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
const char * node_name;
|
||||
size_t length;
|
||||
|
||||
// Expect a token and a colon
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_TOKEN, &node_name, &length);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_COLON, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
|
||||
// copy the node name into the rule
|
||||
rule->node_name = rcutils_strndup(node_name, length, rule->allocator);
|
||||
if (NULL == rule->node_name) {
|
||||
RCL_SET_ERROR_MSG("failed to allocate node name", rule->allocator);
|
||||
return RCL_RET_BAD_ALLOC;
|
||||
}
|
||||
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
/// Start recursive descent parsing of a remap rule.
|
||||
/// \param[in] lex_lookahead a lookahead(2) buffer for the parser to use.
|
||||
/// \param[in,out] rule input a zero intialized rule, output a fully initialized one.
|
||||
/// \return RCL_RET_OK if a valid rule was parsed, or
|
||||
/// \return RCL_RET_INVALID_REMAP_RULE if the argument is not a valid rule, or
|
||||
/// \return RCL_RET_BAD_ALLOC if an allocation failed, or
|
||||
/// \return RLC_RET_ERROR if an unspecified error occurred.
|
||||
/// \internal
|
||||
RCL_LOCAL
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_begin_remap_rule(
|
||||
rcl_lexer_lookahead2_t * lex_lookahead,
|
||||
rcl_remap_t * rule)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexeme_t lexeme1;
|
||||
rcl_lexeme_t lexeme2;
|
||||
|
||||
// Check for optional nodename prefix
|
||||
ret = rcl_lexer_lookahead2_peek2(lex_lookahead, &lexeme1, &lexeme2);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
if (RCL_LEXEME_TOKEN == lexeme1 && RCL_LEXEME_COLON == lexeme2) {
|
||||
ret = _rcl_parse_remap_nodename_prefix(lex_lookahead, rule);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = rcl_lexer_lookahead2_peek(lex_lookahead, &lexeme1);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// What type of rule is this (node name replacement, namespace replacement, or name remap)?
|
||||
if (RCL_LEXEME_NODE == lexeme1) {
|
||||
ret = _rcl_parse_remap_nodename_replacement(lex_lookahead, rule);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
} else if (RCL_LEXEME_NS == lexeme1) {
|
||||
ret = _rcl_parse_remap_namespace_replacement(lex_lookahead, rule);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = _rcl_parse_remap_name_remap(lex_lookahead, rule);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all characters in string have been consumed
|
||||
ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_EOF, NULL, NULL);
|
||||
if (RCL_RET_WRONG_LEXEME == ret) {
|
||||
return RCL_RET_INVALID_REMAP_RULE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
_rcl_parse_remap_rule(
|
||||
const char * arg,
|
||||
rcl_allocator_t allocator,
|
||||
rcl_remap_t * output_rule)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT, allocator);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(output_rule, RCL_RET_INVALID_ARGUMENT, allocator);
|
||||
|
||||
rcl_ret_t ret;
|
||||
|
||||
output_rule->allocator = allocator;
|
||||
rcl_lexer_lookahead2_t lex_lookahead = rcl_get_zero_initialized_lexer_lookahead2();
|
||||
|
||||
ret = rcl_lexer_lookahead2_init(&lex_lookahead, arg, allocator);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = _rcl_parse_remap_begin_remap_rule(&lex_lookahead, output_rule);
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
// cleanup stuff, but return the original error code
|
||||
if (RCL_RET_OK != rcl_remap_fini(output_rule)) {
|
||||
RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME, "Failed to fini remap rule after error occurred");
|
||||
}
|
||||
if (RCL_RET_OK != rcl_lexer_lookahead2_fini(&lex_lookahead)) {
|
||||
RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME, "Failed to fini lookahead2 after error occurred");
|
||||
}
|
||||
} else {
|
||||
ret = rcl_lexer_lookahead2_fini(&lex_lookahead);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
655
rcl/src/rcl/lexer.c
Normal file
655
rcl/src/rcl/lexer.c
Normal file
|
@ -0,0 +1,655 @@
|
|||
// Copyright 2018 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/lexer.h"
|
||||
|
||||
/* The lexer tries to find a lexeme in a string.
|
||||
* It looks at one character at a time, and uses that character's value to decide how to transition
|
||||
* a state machine.
|
||||
* A transition is taken if a character's ASCII value falls within its range.
|
||||
* There is never more than one matching transition.
|
||||
*
|
||||
* If no transition matches then it uses a state's '<else,M>' transition.
|
||||
* Every state has exactly one '<else,M>' transition.
|
||||
* In the diagram below all states have an `<else,0>` to T_NONE unless otherwise specified.
|
||||
*
|
||||
* When a transition is taken it causes the lexer to move to another character in the string.
|
||||
* Normal transitions always move the lexer forwards one character.
|
||||
* '<else,M>' transitions may cause the lexer to move forwards 1, or backwards N.
|
||||
* The movement M is written as M = 1 + N so it can be stored in an unsigned integer.
|
||||
* For example, an `<else>` transition with M = 0 moves the lexer forwards 1 character, M = 1 keeps
|
||||
* the lexer at the current character, and M = 2 moves the lexer backwards one character.
|
||||
|
||||
digraph remapping_lexer {
|
||||
rankdir=LR;
|
||||
node [shape = box, fontsize = 7];
|
||||
T_TILDE_SLASH
|
||||
T_URL_SERVICE
|
||||
T_URL_TOPIC
|
||||
T_COLON
|
||||
T_NODE
|
||||
T_NS
|
||||
T_SEPARATOR
|
||||
T_BR1
|
||||
T_BR2
|
||||
T_BR3
|
||||
T_BR4
|
||||
T_BR5
|
||||
T_BR6
|
||||
T_BR7
|
||||
T_BR8
|
||||
T_BR9
|
||||
T_TOKEN
|
||||
T_FORWARD_SLASH
|
||||
T_WILD_ONE
|
||||
T_WILD_MULTI
|
||||
T_EOF
|
||||
T_NONE
|
||||
node [shape = circle];
|
||||
S0 -> T_FORWARD_SLASH [ label = "/"];
|
||||
S0 -> S1 [ label = "\\"];
|
||||
S0 -> S2 [ label = "~"];
|
||||
S0 -> S3 [ label = "_" ];
|
||||
S0 -> S8 [ label = "a-qs-zA-Z"];
|
||||
S0 -> S10 [ label = "r"];
|
||||
S0 -> S29 [ label = "*"];
|
||||
S0 -> S30 [ label = ":"];
|
||||
S1 -> T_BR1 [ label = "1"];
|
||||
S1 -> T_BR2 [ label = "2"];
|
||||
S1 -> T_BR3 [ label = "3"];
|
||||
S1 -> T_BR4 [ label = "4"];
|
||||
S1 -> T_BR5 [ label = "5"];
|
||||
S1 -> T_BR6 [ label = "6"];
|
||||
S1 -> T_BR7 [ label = "7"];
|
||||
S1 -> T_BR8 [ label = "8"];
|
||||
S1 -> T_BR9 [ label = "9"];
|
||||
S2 -> T_TILDE_SLASH [ label ="/" ];
|
||||
S3 -> S4 [ label = "_" ];
|
||||
S3 -> S9 [ label = "<else,1>", color = crimson, fontcolor = crimson];
|
||||
S4 -> S5 [ label = "n" ];
|
||||
S5 -> T_NS [ label = "s"];
|
||||
S5 -> S6 [ label = "o" ];
|
||||
S6 -> S7 [ label = "d" ];
|
||||
S7 -> T_NODE [ label = "e"];
|
||||
S8 -> T_TOKEN [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S8 -> S8 [ label = "a-zA-Z0-9"];
|
||||
S8 -> S9 [ label = "_"];
|
||||
S9 -> T_TOKEN [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S9 -> S8 [ label = "a-zA-Z0-9"];
|
||||
S10 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S10 -> S11 [ label = "o"];
|
||||
S11 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S11 -> S12 [ label = "s"];
|
||||
S12 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S12 -> S13 [ label = "t"];
|
||||
S12 -> S20 [ label = "s"];
|
||||
S13 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S13 -> S14 [ label = "o"];
|
||||
S14 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S14 -> S15 [ label = "p"];
|
||||
S15 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S15 -> S16 [ label = "i"];
|
||||
S16 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S16 -> S17 [ label = "c"];
|
||||
S17 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S17 -> S18 [ label = ":"];
|
||||
S18 -> S19 [ label = "/"];
|
||||
S18 -> S8 [ label = "<else,2>", color=crimson, fontcolor=crimson];
|
||||
S19 -> T_URL_TOPIC [ label = "/"];
|
||||
S19 -> S8 [ label = "<else,3>", color=crimson, fontcolor=crimson];
|
||||
S20 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S20 -> S21 [ label = "e"];
|
||||
S21 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S21 -> S22 [ label = "r"];
|
||||
S22 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S22 -> S23 [ label = "v"];
|
||||
S23 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S23 -> S24 [ label = "i"];
|
||||
S24 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S24 -> S25 [ label = "c"];
|
||||
S25 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S25 -> S26 [ label = "e"];
|
||||
S26 -> S27 [ label = ":"];
|
||||
S26 -> S8 [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S27 -> S28 [ label = "/"];
|
||||
S27 -> S8 [ label = "<else,2>", color=crimson, fontcolor=crimson];
|
||||
S28 -> T_URL_SERVICE [ label = "/"];
|
||||
S28 -> S8 [ label = "<else,3>", color=crimson, fontcolor=crimson];
|
||||
S29 -> T_WILD_MULTI[ label = "*"];
|
||||
S29 -> T_WILD_ONE [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
S30 -> T_SEPARATOR [ label = "="];
|
||||
S30 -> T_COLON [ label = "<else,1>", color=crimson, fontcolor=crimson];
|
||||
}
|
||||
*/
|
||||
|
||||
/// Represents a transition from one state to another
|
||||
/// \internal
|
||||
typedef struct rcl_lexer_transition_t
|
||||
{
|
||||
/// Index of a state to transition to
|
||||
const unsigned char to_state;
|
||||
/// Start of a range of chars (inclusive) which activates this transition
|
||||
const char range_start;
|
||||
/// End of a range of chars (inclusive) which activates this transition
|
||||
const char range_end;
|
||||
} rcl_lexer_transition_t;
|
||||
|
||||
/// Represents a non-terminal state
|
||||
/// \internal
|
||||
typedef struct rcl_lexer_state_t
|
||||
{
|
||||
/// Transition to this state if no other transition matches
|
||||
const unsigned char else_state;
|
||||
/// Movement associated with taking else state
|
||||
const unsigned char else_movement;
|
||||
/// Transitions in the state machine (NULL value at end of array)
|
||||
const rcl_lexer_transition_t transitions[11];
|
||||
} rcl_lexer_state_t;
|
||||
|
||||
#define S0 0u
|
||||
#define S1 1u
|
||||
#define S2 2u
|
||||
#define S3 3u
|
||||
#define S4 4u
|
||||
#define S5 5u
|
||||
#define S6 6u
|
||||
#define S7 7u
|
||||
#define S8 8u
|
||||
#define S9 9u
|
||||
#define S10 10u
|
||||
#define S11 11u
|
||||
#define S12 12u
|
||||
#define S13 13u
|
||||
#define S14 14u
|
||||
#define S15 15u
|
||||
#define S16 16u
|
||||
#define S17 17u
|
||||
#define S18 18u
|
||||
#define S19 19u
|
||||
#define S20 20u
|
||||
#define S21 21u
|
||||
#define S22 22u
|
||||
#define S23 23u
|
||||
#define S24 24u
|
||||
#define S25 25u
|
||||
#define S26 26u
|
||||
#define S27 27u
|
||||
#define S28 28u
|
||||
#define S29 29u
|
||||
#define S30 30u
|
||||
#define LAST_STATE S30
|
||||
|
||||
#define T_TILDE_SLASH 31u
|
||||
#define T_URL_SERVICE 32u
|
||||
#define T_URL_TOPIC 33u
|
||||
#define T_COLON 34u
|
||||
#define T_NODE 35u
|
||||
#define T_NS 36u
|
||||
#define T_SEPARATOR 37u
|
||||
#define T_BR1 38u
|
||||
#define T_BR2 39u
|
||||
#define T_BR3 40u
|
||||
#define T_BR4 41u
|
||||
#define T_BR5 42u
|
||||
#define T_BR6 43u
|
||||
#define T_BR7 44u
|
||||
#define T_BR8 45u
|
||||
#define T_BR9 46u
|
||||
#define T_TOKEN 47u
|
||||
#define T_FORWARD_SLASH 48u
|
||||
#define T_WILD_ONE 49u
|
||||
#define T_WILD_MULTI 50u
|
||||
#define T_EOF 51u
|
||||
#define T_NONE 52u
|
||||
|
||||
// used to figure out if a state is terminal or not
|
||||
#define FIRST_TERMINAL T_TILDE_SLASH
|
||||
#define LAST_TERMINAL T_NONE
|
||||
|
||||
// Used to mark where the last transition is in a state
|
||||
#define END_TRANSITIONS {0, '\0', '\0'}
|
||||
|
||||
static const rcl_lexer_state_t g_states[LAST_STATE + 1] =
|
||||
{
|
||||
// S0
|
||||
{
|
||||
T_NONE,
|
||||
0u,
|
||||
{
|
||||
{T_FORWARD_SLASH, '/', '/'},
|
||||
{S1, '\\', '\\'},
|
||||
{S2, '~', '~'},
|
||||
{S3, '_', '_'},
|
||||
{S8, 'a', 'q'},
|
||||
{S8, 's', 'z'},
|
||||
{S8, 'A', 'Z'},
|
||||
{S10, 'r', 'r'},
|
||||
{S29, '*', '*'},
|
||||
{S30, ':', ':'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S1
|
||||
{
|
||||
T_NONE,
|
||||
0u,
|
||||
{
|
||||
{T_BR1, '1', '1'},
|
||||
{T_BR2, '2', '2'},
|
||||
{T_BR3, '3', '3'},
|
||||
{T_BR4, '4', '4'},
|
||||
{T_BR5, '5', '5'},
|
||||
{T_BR6, '6', '6'},
|
||||
{T_BR7, '7', '7'},
|
||||
{T_BR8, '8', '8'},
|
||||
{T_BR9, '9', '9'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S2
|
||||
{
|
||||
T_NONE,
|
||||
0u,
|
||||
{
|
||||
{T_TILDE_SLASH, '/', '/'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S3
|
||||
{
|
||||
S9,
|
||||
1u,
|
||||
{
|
||||
{S4, '_', '_'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S4
|
||||
{
|
||||
T_NONE,
|
||||
0u,
|
||||
{
|
||||
{S5, 'n', 'n'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S5
|
||||
{
|
||||
T_NONE,
|
||||
0u,
|
||||
{
|
||||
{T_NS, 's', 's'},
|
||||
{S6, 'o', 'o'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S6
|
||||
{
|
||||
T_NONE,
|
||||
0u,
|
||||
{
|
||||
{S7, 'd', 'd'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S7
|
||||
{
|
||||
T_NONE,
|
||||
0u,
|
||||
{
|
||||
{T_NODE, 'e', 'e'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S8
|
||||
{
|
||||
T_TOKEN,
|
||||
1u,
|
||||
{
|
||||
{S8, 'a', 'z'},
|
||||
{S8, 'A', 'Z'},
|
||||
{S8, '0', '9'},
|
||||
{S9, '_', '_'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S9
|
||||
{
|
||||
T_TOKEN,
|
||||
1u,
|
||||
{
|
||||
{S8, 'a', 'z'},
|
||||
{S8, 'A', 'Z'},
|
||||
{S8, '0', '9'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S10
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S11, 'o', 'o'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S11
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S12, 's', 's'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S12
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S13, 't', 't'},
|
||||
{S20, 's', 's'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S13
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S14, 'o', 'o'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S14
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S15, 'p', 'p'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S15
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S16, 'i', 'i'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S16
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S17, 'c', 'c'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S17
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S18, ':', ':'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S18
|
||||
{
|
||||
S8,
|
||||
2u,
|
||||
{
|
||||
{S19, '/', '/'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S19
|
||||
{
|
||||
S8,
|
||||
3u,
|
||||
{
|
||||
{T_URL_TOPIC, '/', '/'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S20
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S21, 'e', 'e'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S21
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S22, 'r', 'r'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S22
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S23, 'v', 'v'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S23
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S24, 'i', 'i'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S24
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S25, 'c', 'c'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S25
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S26, 'e', 'e'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S26
|
||||
{
|
||||
S8,
|
||||
1u,
|
||||
{
|
||||
{S27, ':', ':'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S27
|
||||
{
|
||||
S8,
|
||||
2u,
|
||||
{
|
||||
{S28, '/', '/'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S28
|
||||
{
|
||||
S8,
|
||||
3u,
|
||||
{
|
||||
{T_URL_SERVICE, '/', '/'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S29
|
||||
{
|
||||
T_WILD_ONE,
|
||||
1u,
|
||||
{
|
||||
{T_WILD_MULTI, '*', '*'},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
// S30
|
||||
{
|
||||
T_COLON,
|
||||
1u,
|
||||
{
|
||||
{T_SEPARATOR, '=', '='},
|
||||
END_TRANSITIONS
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static const rcl_lexeme_t g_terminals[LAST_TERMINAL + 1] = {
|
||||
// 0
|
||||
RCL_LEXEME_TILDE_SLASH,
|
||||
// 1
|
||||
RCL_LEXEME_URL_SERVICE,
|
||||
// 2
|
||||
RCL_LEXEME_URL_TOPIC,
|
||||
// 3
|
||||
RCL_LEXEME_COLON,
|
||||
// 4
|
||||
RCL_LEXEME_NODE,
|
||||
// 5
|
||||
RCL_LEXEME_NS,
|
||||
// 6
|
||||
RCL_LEXEME_SEPARATOR,
|
||||
// 7
|
||||
RCL_LEXEME_BR1,
|
||||
// 8
|
||||
RCL_LEXEME_BR2,
|
||||
// 9
|
||||
RCL_LEXEME_BR3,
|
||||
// 10
|
||||
RCL_LEXEME_BR4,
|
||||
// 11
|
||||
RCL_LEXEME_BR5,
|
||||
// 12
|
||||
RCL_LEXEME_BR6,
|
||||
// 13
|
||||
RCL_LEXEME_BR7,
|
||||
// 14
|
||||
RCL_LEXEME_BR8,
|
||||
// 15
|
||||
RCL_LEXEME_BR9,
|
||||
// 16
|
||||
RCL_LEXEME_TOKEN,
|
||||
// 17
|
||||
RCL_LEXEME_FORWARD_SLASH,
|
||||
// 18
|
||||
RCL_LEXEME_WILD_ONE,
|
||||
// 19
|
||||
RCL_LEXEME_WILD_MULTI,
|
||||
// 20
|
||||
RCL_LEXEME_EOF,
|
||||
// 21
|
||||
RCL_LEXEME_NONE,
|
||||
};
|
||||
|
||||
rcl_ret_t
|
||||
rcl_lexer_analyze(
|
||||
const char * text,
|
||||
rcl_allocator_t alloc,
|
||||
rcl_lexeme_t * lexeme,
|
||||
size_t * length)
|
||||
{
|
||||
RCL_CHECK_ALLOCATOR_WITH_MSG(&alloc, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(text, RCL_RET_INVALID_ARGUMENT, alloc);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(lexeme, RCL_RET_INVALID_ARGUMENT, alloc);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(length, RCL_RET_INVALID_ARGUMENT, alloc);
|
||||
|
||||
*length = 0u;
|
||||
|
||||
if ('\0' == text[0u]) {
|
||||
// Early exit if string is empty
|
||||
*lexeme = RCL_LEXEME_EOF;
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
const rcl_lexer_state_t * state;
|
||||
char current_char;
|
||||
size_t next_state = S0;
|
||||
size_t movement;
|
||||
|
||||
// Analyze one character at a time until lexeme is found
|
||||
do {
|
||||
if (next_state > LAST_STATE) {
|
||||
// Should never happen
|
||||
RCL_SET_ERROR_MSG("Internal lexer bug: next state does not exist", alloc);
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
state = &(g_states[next_state]);
|
||||
current_char = text[*length];
|
||||
next_state = 0u;
|
||||
movement = 0u;
|
||||
|
||||
// Look for a transition that contains this character in its range
|
||||
size_t transition_idx = 0u;
|
||||
const rcl_lexer_transition_t * transition;
|
||||
do {
|
||||
transition = &(state->transitions[transition_idx]);
|
||||
if (transition->range_start <= current_char && transition->range_end >= current_char) {
|
||||
next_state = transition->to_state;
|
||||
break;
|
||||
}
|
||||
++transition_idx;
|
||||
} while (0u != transition->to_state);
|
||||
|
||||
// if no transition was found, take the else transition
|
||||
if (0u == next_state) {
|
||||
next_state = state->else_state;
|
||||
movement = state->else_movement;
|
||||
}
|
||||
|
||||
// Move the lexer to another character in the string
|
||||
if (0u == movement) {
|
||||
// Go forwards 1 char
|
||||
++(*length);
|
||||
} else {
|
||||
// Go backwards N chars
|
||||
if (movement - 1u > *length) {
|
||||
// Should never happen
|
||||
RCL_SET_ERROR_MSG("Internal lexer bug: movement would read before start of string", alloc);
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
*length -= movement - 1u;
|
||||
}
|
||||
} while (next_state < FIRST_TERMINAL);
|
||||
|
||||
if (FIRST_TERMINAL > next_state || next_state - FIRST_TERMINAL > LAST_TERMINAL) {
|
||||
// Should never happen
|
||||
RCL_SET_ERROR_MSG("Internal lexer bug: terminal state does not exist", alloc);
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
*lexeme = g_terminals[next_state - FIRST_TERMINAL];
|
||||
return RCL_RET_OK;
|
||||
}
|
237
rcl/src/rcl/lexer_lookahead.c
Normal file
237
rcl/src/rcl/lexer_lookahead.c
Normal file
|
@ -0,0 +1,237 @@
|
|||
// Copyright 2018 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/lexer_lookahead.h"
|
||||
|
||||
struct rcl_lexer_lookahead2_impl_t
|
||||
{
|
||||
// Text that is being analyzed for lexemes
|
||||
const char * text;
|
||||
// Where in the text analysis is being performed
|
||||
size_t text_idx;
|
||||
|
||||
// first character of lexeme
|
||||
size_t start[2];
|
||||
// One past last character of lexeme
|
||||
size_t end[2];
|
||||
// Type of lexeme
|
||||
rcl_lexeme_t type[2];
|
||||
|
||||
// Allocator to use if an error occurrs
|
||||
rcl_allocator_t allocator;
|
||||
};
|
||||
|
||||
rcl_lexer_lookahead2_t
|
||||
rcl_get_zero_initialized_lexer_lookahead2()
|
||||
{
|
||||
static rcl_lexer_lookahead2_t zero_initialized = {
|
||||
.impl = NULL,
|
||||
};
|
||||
return zero_initialized;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_init(
|
||||
rcl_lexer_lookahead2_t * buffer,
|
||||
const char * text,
|
||||
rcl_allocator_t allocator)
|
||||
{
|
||||
RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(buffer, RCL_RET_INVALID_ARGUMENT, allocator);
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(text, RCL_RET_INVALID_ARGUMENT, allocator);
|
||||
if (NULL != buffer->impl) {
|
||||
RCL_SET_ERROR_MSG("buffer must be zero initialized", allocator);
|
||||
return RCL_RET_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
buffer->impl = allocator.allocate(sizeof(struct rcl_lexer_lookahead2_impl_t), allocator.state);
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
buffer->impl, "Failed to allocate lookahead impl", return RCL_RET_BAD_ALLOC, allocator);
|
||||
|
||||
buffer->impl->text = text;
|
||||
buffer->impl->text_idx = 0u;
|
||||
buffer->impl->start[0] = 0u;
|
||||
buffer->impl->start[1] = 0u;
|
||||
buffer->impl->end[0] = 0u;
|
||||
buffer->impl->end[1] = 0u;
|
||||
buffer->impl->type[0] = RCL_LEXEME_NONE;
|
||||
buffer->impl->type[1] = RCL_LEXEME_NONE;
|
||||
buffer->impl->allocator = allocator;
|
||||
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_fini(
|
||||
rcl_lexer_lookahead2_t * buffer)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(buffer, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator());
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
buffer->impl, "buffer finalized twice", return RCL_RET_INVALID_ARGUMENT,
|
||||
rcl_get_default_allocator());
|
||||
RCL_CHECK_ALLOCATOR_WITH_MSG(
|
||||
&(buffer->impl->allocator), "invalid allocator", return RCL_RET_INVALID_ARGUMENT);
|
||||
|
||||
buffer->impl->allocator.deallocate(buffer->impl, buffer->impl->allocator.state);
|
||||
buffer->impl = NULL;
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_peek(
|
||||
rcl_lexer_lookahead2_t * buffer,
|
||||
rcl_lexeme_t * next_type)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(buffer, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator());
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
buffer->impl, "buffer not initialized", return RCL_RET_INVALID_ARGUMENT,
|
||||
rcl_get_default_allocator());
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(next_type, RCL_RET_INVALID_ARGUMENT, buffer->impl->allocator);
|
||||
|
||||
rcl_ret_t ret;
|
||||
size_t length;
|
||||
|
||||
if (buffer->impl->text_idx >= buffer->impl->end[0]) {
|
||||
// No buffered lexeme; get one
|
||||
ret = rcl_lexer_analyze(
|
||||
rcl_lexer_lookahead2_get_text(buffer),
|
||||
buffer->impl->allocator,
|
||||
&(buffer->impl->type[0]),
|
||||
&length);
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
buffer->impl->start[0] = buffer->impl->text_idx;
|
||||
buffer->impl->end[0] = buffer->impl->start[0] + length;
|
||||
}
|
||||
|
||||
*next_type = buffer->impl->type[0];
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_peek2(
|
||||
rcl_lexer_lookahead2_t * buffer,
|
||||
rcl_lexeme_t * next_type1,
|
||||
rcl_lexeme_t * next_type2)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
// Peek 1 ahead first (reusing its error checking for buffer and next_type1)
|
||||
ret = rcl_lexer_lookahead2_peek(buffer, next_type1);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(next_type2, RCL_RET_INVALID_ARGUMENT, buffer->impl->allocator);
|
||||
|
||||
size_t length;
|
||||
|
||||
if (buffer->impl->text_idx >= buffer->impl->end[1]) {
|
||||
// No buffered lexeme; get one
|
||||
ret = rcl_lexer_analyze(
|
||||
&(buffer->impl->text[buffer->impl->end[0]]),
|
||||
buffer->impl->allocator,
|
||||
&(buffer->impl->type[1]),
|
||||
&length);
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
buffer->impl->start[1] = buffer->impl->end[0];
|
||||
buffer->impl->end[1] = buffer->impl->start[1] + length;
|
||||
}
|
||||
|
||||
*next_type2 = buffer->impl->type[1];
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_accept(
|
||||
rcl_lexer_lookahead2_t * buffer,
|
||||
const char ** lexeme_text,
|
||||
size_t * lexeme_text_length)
|
||||
{
|
||||
RCL_CHECK_ARGUMENT_FOR_NULL(buffer, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator());
|
||||
RCL_CHECK_FOR_NULL_WITH_MSG(
|
||||
buffer->impl, "buffer not initialized", return RCL_RET_INVALID_ARGUMENT,
|
||||
rcl_get_default_allocator());
|
||||
if (
|
||||
(NULL == lexeme_text && NULL != lexeme_text_length) ||
|
||||
(NULL != lexeme_text && NULL == lexeme_text_length))
|
||||
{
|
||||
RCL_SET_ERROR_MSG("text and length must both be set or both be NULL", buffer->impl->allocator);
|
||||
return RCL_RET_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (RCL_LEXEME_EOF == buffer->impl->type[0]) {
|
||||
// Reached EOF, nothing to accept
|
||||
if (NULL != lexeme_text && NULL != lexeme_text_length) {
|
||||
*lexeme_text = rcl_lexer_lookahead2_get_text(buffer);
|
||||
*lexeme_text_length = 0u;
|
||||
}
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
if (buffer->impl->text_idx >= buffer->impl->end[0]) {
|
||||
RCL_SET_ERROR_MSG("no lexeme to accept", buffer->impl->allocator);
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
|
||||
if (NULL != lexeme_text && NULL != lexeme_text_length) {
|
||||
*lexeme_text = &(buffer->impl->text[buffer->impl->start[0]]);
|
||||
*lexeme_text_length = buffer->impl->end[0] - buffer->impl->start[0];
|
||||
}
|
||||
|
||||
// Advance lexer position
|
||||
buffer->impl->text_idx = buffer->impl->end[0];
|
||||
|
||||
// Move second lexeme in buffer to first position
|
||||
buffer->impl->start[0] = buffer->impl->start[1];
|
||||
buffer->impl->end[0] = buffer->impl->end[1];
|
||||
buffer->impl->type[0] = buffer->impl->type[1];
|
||||
|
||||
return RCL_RET_OK;
|
||||
}
|
||||
|
||||
rcl_ret_t
|
||||
rcl_lexer_lookahead2_expect(
|
||||
rcl_lexer_lookahead2_t * buffer,
|
||||
rcl_lexeme_t type,
|
||||
const char ** lexeme_text,
|
||||
size_t * lexeme_text_length)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexeme_t lexeme;
|
||||
|
||||
ret = rcl_lexer_lookahead2_peek(buffer, &lexeme);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return ret;
|
||||
}
|
||||
if (type != lexeme) {
|
||||
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
|
||||
buffer->impl->allocator, "Expected %d got %d at %lu", type, lexeme, buffer->impl->text_idx);
|
||||
return RCL_RET_WRONG_LEXEME;
|
||||
}
|
||||
return rcl_lexer_lookahead2_accept(buffer, lexeme_text, lexeme_text_length);
|
||||
}
|
||||
|
||||
const char *
|
||||
rcl_lexer_lookahead2_get_text(
|
||||
const rcl_lexer_lookahead2_t * buffer)
|
||||
{
|
||||
return &(buffer->impl->text[buffer->impl->text_idx]);
|
||||
}
|
|
@ -67,6 +67,22 @@ function(test_target_function)
|
|||
AMENT_DEPENDENCIES ${rmw_implementation}
|
||||
)
|
||||
|
||||
rcl_add_custom_gtest(test_lexer${target_suffix}
|
||||
SRCS rcl/test_lexer.cpp
|
||||
ENV ${extra_test_env}
|
||||
APPEND_LIBRARY_DIRS ${extra_lib_dirs}
|
||||
LIBRARIES ${PROJECT_NAME} ${extra_test_libraries}
|
||||
AMENT_DEPENDENCIES ${rmw_implementation}
|
||||
)
|
||||
|
||||
rcl_add_custom_gtest(test_lexer_lookahead${target_suffix}
|
||||
SRCS rcl/test_lexer_lookahead.cpp
|
||||
ENV ${extra_test_env}
|
||||
APPEND_LIBRARY_DIRS ${extra_lib_dirs}
|
||||
LIBRARIES ${PROJECT_NAME} ${extra_test_libraries}
|
||||
AMENT_DEPENDENCIES ${rmw_implementation}
|
||||
)
|
||||
|
||||
set(SKIP_TEST "")
|
||||
# TODO(wjwwood): remove this when the graph API works properly for connext dynamic
|
||||
if(
|
||||
|
|
|
@ -95,6 +95,11 @@ TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), check_valid_vs_inval
|
|||
EXPECT_TRUE(is_valid_arg("foo:=/bar"));
|
||||
EXPECT_TRUE(is_valid_arg("/foo123:=/bar123"));
|
||||
EXPECT_TRUE(is_valid_arg("node:/foo123:=/bar123"));
|
||||
EXPECT_TRUE(is_valid_arg("rostopic:=/foo/bar"));
|
||||
EXPECT_TRUE(is_valid_arg("rosservice:=baz"));
|
||||
EXPECT_TRUE(is_valid_arg("rostopic://rostopic:=rosservice"));
|
||||
EXPECT_TRUE(is_valid_arg("rostopic:///rosservice:=rostopic"));
|
||||
EXPECT_TRUE(is_valid_arg("rostopic:///foo/bar:=baz"));
|
||||
|
||||
EXPECT_FALSE(is_valid_arg(":="));
|
||||
EXPECT_FALSE(is_valid_arg("foo:="));
|
||||
|
@ -110,6 +115,8 @@ TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), check_valid_vs_inval
|
|||
EXPECT_FALSE(is_valid_arg("foo:=/b ar"));
|
||||
EXPECT_FALSE(is_valid_arg("f{oo:=/bar"));
|
||||
EXPECT_FALSE(is_valid_arg("foo:=/b}ar"));
|
||||
EXPECT_FALSE(is_valid_arg("rostopic://:=rosservice"));
|
||||
EXPECT_FALSE(is_valid_arg("rostopic::=rosservice"));
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_no_args) {
|
||||
|
|
331
rcl/test/rcl/test_lexer.cpp
Normal file
331
rcl/test/rcl/test_lexer.cpp
Normal file
|
@ -0,0 +1,331 @@
|
|||
// Copyright 2018 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rcl/lexer.h"
|
||||
|
||||
#ifdef RMW_IMPLEMENTATION
|
||||
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
|
||||
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
|
||||
#else
|
||||
# define CLASSNAME(NAME, SUFFIX) NAME
|
||||
#endif
|
||||
|
||||
class CLASSNAME (TestLexerFixture, RMW_IMPLEMENTATION) : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Not using a function so gtest failure output shows the line number where the macro is used
|
||||
#define EXPECT_LEX(expected_lexeme, expected_text, text) \
|
||||
do { \
|
||||
rcl_lexeme_t actual_lexeme; \
|
||||
size_t length; \
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator(); \
|
||||
rcl_ret_t ret = rcl_lexer_analyze(text, allocator, &actual_lexeme, &length); \
|
||||
ASSERT_EQ(RCL_RET_OK, ret); \
|
||||
EXPECT_EQ(expected_lexeme, actual_lexeme); \
|
||||
std::string actual_text(text, length); \
|
||||
EXPECT_STREQ(expected_text, actual_text.c_str()); \
|
||||
} while (false)
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_token)
|
||||
{
|
||||
// Things get recognized as tokens whether input ends or non token characters come after them
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "foo", "foo");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "foo", "foo:");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "foo_", "foo_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "foo_", "foo_:");
|
||||
|
||||
// Check full range for starting character
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a", "a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "b", "b");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "c", "c");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "d", "d");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "e", "e");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "f", "f");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "g", "g");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "h", "h");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "i", "i");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "j", "j");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "k", "k");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "l", "l");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "m", "m");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "n", "n");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "o", "o");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "p", "p");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "q", "q");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "r", "r");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "s", "s");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "t", "t");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "u", "u");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "v", "v");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "w", "w");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "x", "x");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "y", "y");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "z", "z");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "A", "A");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "B", "B");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "C", "C");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "D", "D");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "E", "E");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "F", "F");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "G", "G");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "H", "H");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "I", "I");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "J", "J");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "K", "K");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "L", "L");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "M", "M");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "N", "N");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "O", "O");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "P", "P");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "Q", "Q");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "R", "R");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "S", "S");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "T", "T");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "U", "U");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "V", "V");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "W", "W");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "X", "X");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "Y", "Y");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "Z", "Z");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_", "_");
|
||||
|
||||
// Check banned characters adjacent to allowed ones in ASCII
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "@", "@");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "[", "[");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "`", "`");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "{", "{");
|
||||
|
||||
// Tokens cannot start with digits
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "0", "0");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "1", "1");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "2", "2");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "3", "3");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "4", "4");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "5", "5");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "6", "6");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "7", "7");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "8", "8");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "9", "9");
|
||||
|
||||
// Tokens may contain underscores
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_abcd", "_abcd");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "abcd_", "abcd_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "ab_cd", "ab_cd");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_a_b_c_d_", "_a_b_c_d_");
|
||||
|
||||
// Tokens cannot contain double underscores
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_a_", "_a__bcd");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a_", "a__bcd");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "A_", "A__bcd");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__a", "__a");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__A", "__A");
|
||||
|
||||
// Tokens may contain digits
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_0_", "_0_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_1_", "_1_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_2_", "_2_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_3_", "_3_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_4_", "_4_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_5_", "_5_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_6_", "_6_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_7_", "_7_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_8_", "_8_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_9_", "_9_");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a0a", "a0a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a1a", "a1a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a2a", "a2a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a3a", "a3a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a4a", "a4a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a5a", "a5a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a6a", "a6a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a7a", "a7a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a8a", "a8a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a9a", "a9a");
|
||||
|
||||
// Tokens may end with digits
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_0", "_0");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_1", "_1");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_2", "_2");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_3", "_3");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_4", "_4");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_5", "_5");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_6", "_6");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_7", "_7");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_8", "_8");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_9", "_9");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a0", "a0");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a1", "a1");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a2", "a2");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a3", "a3");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a4", "a4");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a5", "a5");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a6", "a6");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a7", "a7");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a8", "a8");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "a9", "a9");
|
||||
|
||||
// Things that almost look like a url scheme but are actually tokens
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "ro", "ro");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "ros", "ros");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "ross", "ross");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosse", "rosse");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosser", "rosser");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosserv", "rosserv");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosservi", "rosservi");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosservic", "rosservic");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosservice", "rosservice");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosservice", "rosservice:");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosservice", "rosservice:=");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosservice", "rosservice:/");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosservice", "rosservice:/a");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rost", "rost");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rosto", "rosto");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rostop", "rostop");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rostopi", "rostopi");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rostopic", "rostopic");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rostopic", "rostopic:");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rostopic", "rostopic:=");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rostopic", "rostopic:/");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "rostopic", "rostopic:/a");
|
||||
|
||||
// Tokens may contain uppercase characters
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "ABC", "ABC");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_DEF", "_DEF");
|
||||
EXPECT_LEX(RCL_LEXEME_TOKEN, "_GHI_", "_GHI_");
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_url_scheme)
|
||||
{
|
||||
// No text after scheme
|
||||
EXPECT_LEX(RCL_LEXEME_URL_SERVICE, "rosservice://", "rosservice://");
|
||||
EXPECT_LEX(RCL_LEXEME_URL_TOPIC, "rostopic://", "rostopic://");
|
||||
|
||||
// Some text after scheme
|
||||
EXPECT_LEX(RCL_LEXEME_URL_SERVICE, "rosservice://", "rosservice://abcd");
|
||||
EXPECT_LEX(RCL_LEXEME_URL_SERVICE, "rosservice://", "rosservice:///");
|
||||
EXPECT_LEX(RCL_LEXEME_URL_TOPIC, "rostopic://", "rostopic://abcd");
|
||||
EXPECT_LEX(RCL_LEXEME_URL_TOPIC, "rostopic://", "rostopic:///");
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_backreferences)
|
||||
{
|
||||
// No text after backreference
|
||||
EXPECT_LEX(RCL_LEXEME_BR1, "\\1", "\\1");
|
||||
EXPECT_LEX(RCL_LEXEME_BR2, "\\2", "\\2");
|
||||
EXPECT_LEX(RCL_LEXEME_BR3, "\\3", "\\3");
|
||||
EXPECT_LEX(RCL_LEXEME_BR4, "\\4", "\\4");
|
||||
EXPECT_LEX(RCL_LEXEME_BR5, "\\5", "\\5");
|
||||
EXPECT_LEX(RCL_LEXEME_BR6, "\\6", "\\6");
|
||||
EXPECT_LEX(RCL_LEXEME_BR7, "\\7", "\\7");
|
||||
EXPECT_LEX(RCL_LEXEME_BR8, "\\8", "\\8");
|
||||
EXPECT_LEX(RCL_LEXEME_BR9, "\\9", "\\9");
|
||||
|
||||
// Some text after backreference
|
||||
EXPECT_LEX(RCL_LEXEME_BR1, "\\1", "\\1a");
|
||||
EXPECT_LEX(RCL_LEXEME_BR2, "\\2", "\\2a");
|
||||
EXPECT_LEX(RCL_LEXEME_BR3, "\\3", "\\3a");
|
||||
EXPECT_LEX(RCL_LEXEME_BR4, "\\4", "\\4a");
|
||||
EXPECT_LEX(RCL_LEXEME_BR5, "\\5", "\\5a");
|
||||
EXPECT_LEX(RCL_LEXEME_BR6, "\\6", "\\6a");
|
||||
EXPECT_LEX(RCL_LEXEME_BR7, "\\7", "\\7a");
|
||||
EXPECT_LEX(RCL_LEXEME_BR8, "\\8", "\\8a");
|
||||
EXPECT_LEX(RCL_LEXEME_BR9, "\\9", "\\9a");
|
||||
|
||||
// Not valid backreferences
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "\\0", "\\0");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "\\a", "\\a");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "\\Z", "\\Z");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "\\_", "\\_");
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_forward_slash)
|
||||
{
|
||||
EXPECT_LEX(RCL_LEXEME_FORWARD_SLASH, "/", "/");
|
||||
EXPECT_LEX(RCL_LEXEME_FORWARD_SLASH, "/", "//");
|
||||
EXPECT_LEX(RCL_LEXEME_FORWARD_SLASH, "/", "/_");
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_wildcards)
|
||||
{
|
||||
EXPECT_LEX(RCL_LEXEME_WILD_ONE, "*", "*");
|
||||
EXPECT_LEX(RCL_LEXEME_WILD_ONE, "*", "*/");
|
||||
EXPECT_LEX(RCL_LEXEME_WILD_MULTI, "**", "**");
|
||||
EXPECT_LEX(RCL_LEXEME_WILD_MULTI, "**", "**/");
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_colon)
|
||||
{
|
||||
EXPECT_LEX(RCL_LEXEME_COLON, ":", ":");
|
||||
EXPECT_LEX(RCL_LEXEME_COLON, ":", ":r");
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_separator)
|
||||
{
|
||||
EXPECT_LEX(RCL_LEXEME_SEPARATOR, ":=", ":=");
|
||||
EXPECT_LEX(RCL_LEXEME_SEPARATOR, ":=", ":=0");
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_ns)
|
||||
{
|
||||
// Has __ns
|
||||
EXPECT_LEX(RCL_LEXEME_NS, "__ns", "__ns");
|
||||
EXPECT_LEX(RCL_LEXEME_NS, "__ns", "__nsssss");
|
||||
|
||||
// Things that are almost __ns
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__", "__");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__n", "__n");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__n!", "__n!");
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_node)
|
||||
{
|
||||
// Has __node
|
||||
EXPECT_LEX(RCL_LEXEME_NODE, "__node", "__node");
|
||||
EXPECT_LEX(RCL_LEXEME_NODE, "__node", "__nodessss");
|
||||
|
||||
// Things that are almost __node
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__", "__");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__n", "__n");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__na", "__na");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__no", "__no");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__noa", "__noa");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__nod", "__nod");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "__noda", "__noda");
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_tilde_slash)
|
||||
{
|
||||
EXPECT_LEX(RCL_LEXEME_TILDE_SLASH, "~/", "~/");
|
||||
EXPECT_LEX(RCL_LEXEME_TILDE_SLASH, "~/", "~//");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "~", "~");
|
||||
EXPECT_LEX(RCL_LEXEME_NONE, "~!", "~!");
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerFixture, RMW_IMPLEMENTATION), test_eof)
|
||||
{
|
||||
EXPECT_LEX(RCL_LEXEME_EOF, "", "");
|
||||
}
|
374
rcl/test/rcl/test_lexer_lookahead.cpp
Normal file
374
rcl/test/rcl/test_lexer_lookahead.cpp
Normal file
|
@ -0,0 +1,374 @@
|
|||
// Copyright 2018 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../scope_exit.hpp"
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/lexer_lookahead.h"
|
||||
|
||||
#ifdef RMW_IMPLEMENTATION
|
||||
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
|
||||
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
|
||||
#else
|
||||
# define CLASSNAME(NAME, SUFFIX) NAME
|
||||
#endif
|
||||
|
||||
class CLASSNAME (TestLexerLookaheadFixture, RMW_IMPLEMENTATION) : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#define SCOPE_LOOKAHEAD2(name, text) \
|
||||
{ \
|
||||
name = rcl_get_zero_initialized_lexer_lookahead2(); \
|
||||
rcl_ret_t ret = rcl_lexer_lookahead2_init(&name, text, rcl_get_default_allocator()); \
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe(); \
|
||||
} \
|
||||
auto __scope_lookahead2_ ## name = make_scope_exit( \
|
||||
[&name]() { \
|
||||
rcl_ret_t ret = rcl_lexer_lookahead2_fini(&buffer); \
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe(); \
|
||||
})
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerLookaheadFixture, RMW_IMPLEMENTATION), test_init_fini_twice)
|
||||
{
|
||||
rcl_lexer_lookahead2_t buffer = rcl_get_zero_initialized_lexer_lookahead2();
|
||||
rcl_ret_t ret = rcl_lexer_lookahead2_init(&buffer, "foobar", rcl_get_default_allocator());
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
|
||||
ret = rcl_lexer_lookahead2_fini(&buffer);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
|
||||
ret = rcl_lexer_lookahead2_fini(&buffer);
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
|
||||
rcl_reset_error();
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerLookaheadFixture, RMW_IMPLEMENTATION), test_init_not_zero_initialized)
|
||||
{
|
||||
rcl_lexer_lookahead2_t buffer;
|
||||
int not_zero = 1;
|
||||
buffer.impl = reinterpret_cast<rcl_lexer_lookahead2_impl_t *>(¬_zero);
|
||||
rcl_ret_t ret = rcl_lexer_lookahead2_init(&buffer, "foobar", rcl_get_default_allocator());
|
||||
EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret);
|
||||
rcl_reset_error();
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerLookaheadFixture, RMW_IMPLEMENTATION), test_peek)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexer_lookahead2_t buffer;
|
||||
SCOPE_LOOKAHEAD2(buffer, "foobar");
|
||||
|
||||
rcl_lexeme_t lexeme = RCL_LEXEME_NONE;
|
||||
|
||||
ret = rcl_lexer_lookahead2_peek(&buffer, &lexeme);
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
EXPECT_EQ(RCL_LEXEME_TOKEN, lexeme);
|
||||
|
||||
// Test again to make sure peek isn't advancing the lexer
|
||||
lexeme = RCL_LEXEME_NONE;
|
||||
ret = rcl_lexer_lookahead2_peek(&buffer, &lexeme);
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
EXPECT_EQ(RCL_LEXEME_TOKEN, lexeme);
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerLookaheadFixture, RMW_IMPLEMENTATION), test_peek2)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexer_lookahead2_t buffer;
|
||||
SCOPE_LOOKAHEAD2(buffer, "foobar/");
|
||||
|
||||
rcl_lexeme_t lexeme1 = RCL_LEXEME_NONE;
|
||||
rcl_lexeme_t lexeme2 = RCL_LEXEME_NONE;
|
||||
|
||||
ret = rcl_lexer_lookahead2_peek2(&buffer, &lexeme1, &lexeme2);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_EQ(RCL_LEXEME_TOKEN, lexeme1);
|
||||
EXPECT_EQ(RCL_LEXEME_FORWARD_SLASH, lexeme2);
|
||||
|
||||
// Test again to make sure peek2 isn't advancing the lexer
|
||||
lexeme1 = RCL_LEXEME_NONE;
|
||||
lexeme2 = RCL_LEXEME_NONE;
|
||||
ret = rcl_lexer_lookahead2_peek2(&buffer, &lexeme1, &lexeme2);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_EQ(RCL_LEXEME_TOKEN, lexeme1);
|
||||
EXPECT_EQ(RCL_LEXEME_FORWARD_SLASH, lexeme2);
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerLookaheadFixture, RMW_IMPLEMENTATION), test_eof)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexer_lookahead2_t buffer;
|
||||
SCOPE_LOOKAHEAD2(buffer, "");
|
||||
|
||||
{
|
||||
rcl_lexeme_t lexeme = RCL_LEXEME_NONE;
|
||||
ret = rcl_lexer_lookahead2_peek(&buffer, &lexeme);
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
EXPECT_EQ(RCL_LEXEME_EOF, lexeme);
|
||||
}
|
||||
{
|
||||
rcl_lexeme_t lexeme1 = RCL_LEXEME_NONE;
|
||||
rcl_lexeme_t lexeme2 = RCL_LEXEME_NONE;
|
||||
ret = rcl_lexer_lookahead2_peek2(&buffer, &lexeme1, &lexeme2);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_EQ(RCL_LEXEME_EOF, lexeme1);
|
||||
EXPECT_EQ(RCL_LEXEME_EOF, lexeme2);
|
||||
}
|
||||
// Accepting keeps the lexer at EOF
|
||||
{
|
||||
EXPECT_EQ(RCL_RET_OK, rcl_lexer_lookahead2_accept(&buffer, NULL, NULL));
|
||||
rcl_lexeme_t lexeme = RCL_LEXEME_NONE;
|
||||
ret = rcl_lexer_lookahead2_peek(&buffer, &lexeme);
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
EXPECT_EQ(RCL_LEXEME_EOF, lexeme);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerLookaheadFixture, RMW_IMPLEMENTATION), test_accept)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexer_lookahead2_t buffer;
|
||||
SCOPE_LOOKAHEAD2(buffer, "foobar/");
|
||||
|
||||
rcl_lexeme_t lexeme = RCL_LEXEME_NONE;
|
||||
const char * lexeme_text;
|
||||
size_t lexeme_text_length;
|
||||
|
||||
// Peek token
|
||||
ret = rcl_lexer_lookahead2_peek(&buffer, &lexeme);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_EQ(RCL_LEXEME_TOKEN, lexeme);
|
||||
|
||||
// accept token
|
||||
ret = rcl_lexer_lookahead2_accept(&buffer, &lexeme_text, &lexeme_text_length);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_STREQ("foobar", std::string(lexeme_text, lexeme_text_length).c_str());
|
||||
|
||||
// peek forward slash
|
||||
ret = rcl_lexer_lookahead2_peek(&buffer, &lexeme);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_EQ(RCL_LEXEME_FORWARD_SLASH, lexeme);
|
||||
|
||||
// accept forward slash
|
||||
ret = rcl_lexer_lookahead2_accept(&buffer, &lexeme_text, &lexeme_text_length);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_STREQ("/", std::string(lexeme_text, lexeme_text_length).c_str());
|
||||
|
||||
// peek eof
|
||||
ret = rcl_lexer_lookahead2_peek(&buffer, &lexeme);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_EQ(RCL_LEXEME_EOF, lexeme);
|
||||
|
||||
// accept eof
|
||||
ret = rcl_lexer_lookahead2_accept(&buffer, &lexeme_text, &lexeme_text_length);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_STREQ("", std::string(lexeme_text, lexeme_text_length).c_str());
|
||||
|
||||
// peek eof again
|
||||
ret = rcl_lexer_lookahead2_peek(&buffer, &lexeme);
|
||||
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_EQ(RCL_LEXEME_EOF, lexeme);
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerLookaheadFixture, RMW_IMPLEMENTATION), test_expect)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexer_lookahead2_t buffer;
|
||||
SCOPE_LOOKAHEAD2(buffer, "node_name:__node:=new_1");
|
||||
const char * lexeme_text;
|
||||
size_t lexeme_text_length;
|
||||
|
||||
ret = rcl_lexer_lookahead2_expect(&buffer, RCL_LEXEME_TOKEN, &lexeme_text, &lexeme_text_length);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe();
|
||||
EXPECT_STREQ("node_name", std::string(lexeme_text, lexeme_text_length).c_str());
|
||||
|
||||
ret = rcl_lexer_lookahead2_expect(
|
||||
&buffer, RCL_LEXEME_FORWARD_SLASH, &lexeme_text, &lexeme_text_length);
|
||||
EXPECT_EQ(RCL_RET_WRONG_LEXEME, ret) << rcl_get_error_string_safe();
|
||||
}
|
||||
|
||||
#define EXPECT_LOOKAHEAD(expected_lexeme, expected_text, buffer) \
|
||||
do { \
|
||||
const char * lexeme_text; \
|
||||
size_t lexeme_text_length; \
|
||||
rcl_lexeme_t lexeme; \
|
||||
ret = rcl_lexer_lookahead2_peek(&buffer, &lexeme); \
|
||||
EXPECT_EQ(expected_lexeme, lexeme); \
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe(); \
|
||||
ret = rcl_lexer_lookahead2_accept(&buffer, &lexeme_text, &lexeme_text_length); \
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string_safe(); \
|
||||
EXPECT_STREQ(expected_text, std::string(lexeme_text, lexeme_text_length).c_str()); \
|
||||
} while (false)
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerLookaheadFixture, RMW_IMPLEMENTATION), test_lex_long_string)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexer_lookahead2_t buffer;
|
||||
SCOPE_LOOKAHEAD2(buffer, ":\\1rostopic://\\2rosservice://~/\\8:=**:*foobar");
|
||||
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_COLON, ":", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_BR1, "\\1", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_URL_TOPIC, "rostopic://", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_BR2, "\\2", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_URL_SERVICE, "rosservice://", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TILDE_SLASH, "~/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_BR8, "\\8", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_WILD_MULTI, "**", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_COLON, ":", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_WILD_ONE, "*", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foobar", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestLexerLookaheadFixture, RMW_IMPLEMENTATION), test_lex_remap_rules)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_lexer_lookahead2_t buffer;
|
||||
{
|
||||
SCOPE_LOOKAHEAD2(buffer, "foo:=bar");
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "bar", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
{
|
||||
SCOPE_LOOKAHEAD2(buffer, "/foo/bar:=fiz/buzz");
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "bar", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "fiz", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "buzz", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
{
|
||||
// Nodename prefix
|
||||
SCOPE_LOOKAHEAD2(buffer, "nodename:~/foo:=foo");
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "nodename", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_COLON, ":", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TILDE_SLASH, "~/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
{
|
||||
// Partial namespace replacement
|
||||
SCOPE_LOOKAHEAD2(buffer, "/foo/**:=/fizz/\\1");
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_WILD_MULTI, "**", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "fizz", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_BR1, "\\1", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
{
|
||||
// Full namespace replacement
|
||||
SCOPE_LOOKAHEAD2(buffer, "/foo/bar/*:=/bar/foo/\\1");
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "bar", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_WILD_ONE, "*", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "bar", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_BR1, "\\1", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
{
|
||||
// Change a base name
|
||||
SCOPE_LOOKAHEAD2(buffer, "**/foo:=\\1/bar");
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_WILD_MULTI, "**", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_BR1, "\\1", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "bar", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
{
|
||||
// Change namespace
|
||||
SCOPE_LOOKAHEAD2(buffer, "__ns:=/new/namespace");
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_NS, "__ns", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "new", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "namespace", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
{
|
||||
// Change node name
|
||||
SCOPE_LOOKAHEAD2(buffer, "__node:=left_camera_driver");
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_NODE, "__node", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "left_camera_driver", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
{
|
||||
// Topic only remap
|
||||
SCOPE_LOOKAHEAD2(buffer, "rostopic://foo/bar:=bar/foo");
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_URL_TOPIC, "rostopic://", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "bar", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "bar", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
{
|
||||
// Service only remap
|
||||
SCOPE_LOOKAHEAD2(buffer, "rosservice:///foo/bar:=/bar/foo");
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_URL_SERVICE, "rosservice://", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "bar", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_SEPARATOR, ":=", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "bar", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_FORWARD_SLASH, "/", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_TOKEN, "foo", buffer);
|
||||
EXPECT_LOOKAHEAD(RCL_LEXEME_EOF, "", buffer);
|
||||
}
|
||||
}
|
|
@ -465,3 +465,39 @@ TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), other_rules_before_noden
|
|||
EXPECT_STREQ("remap_name", output);
|
||||
allocator.deallocate(output, allocator.state);
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), url_scheme_rosservice) {
|
||||
rcl_ret_t ret;
|
||||
rcl_arguments_t global_arguments;
|
||||
SCOPE_ARGS(global_arguments, "process_name", "rosservice://foo:=bar");
|
||||
|
||||
char * output = NULL;
|
||||
ret = rcl_remap_service_name(
|
||||
NULL, &global_arguments, "/ns/foo", "NodeName", "/ns", rcl_get_default_allocator(), &output);
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
ASSERT_STREQ("/ns/bar", output);
|
||||
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
|
||||
|
||||
ret = rcl_remap_topic_name(
|
||||
NULL, &global_arguments, "/ns/foo", "NodeName", "/ns", rcl_get_default_allocator(), &output);
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
EXPECT_EQ(NULL, output);
|
||||
}
|
||||
|
||||
TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), url_scheme_rostopic) {
|
||||
rcl_ret_t ret;
|
||||
rcl_arguments_t global_arguments;
|
||||
SCOPE_ARGS(global_arguments, "process_name", "rostopic://foo:=bar");
|
||||
|
||||
char * output = NULL;
|
||||
ret = rcl_remap_topic_name(
|
||||
NULL, &global_arguments, "/ns/foo", "NodeName", "/ns", rcl_get_default_allocator(), &output);
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
ASSERT_STREQ("/ns/bar", output);
|
||||
rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state);
|
||||
|
||||
ret = rcl_remap_service_name(
|
||||
NULL, &global_arguments, "/ns/foo", "NodeName", "/ns", rcl_get_default_allocator(), &output);
|
||||
EXPECT_EQ(RCL_RET_OK, ret);
|
||||
EXPECT_EQ(NULL, output);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue