Support relative type references in msg files
This commit is contained in:
parent
3d694b20f6
commit
117a4f6348
@ -253,6 +253,11 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods
|
|||||||
|
|
||||||
RULES = parse_grammar(GRAMMAR_IDL)
|
RULES = parse_grammar(GRAMMAR_IDL)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize."""
|
||||||
|
super().__init__()
|
||||||
|
self.typedefs = {}
|
||||||
|
|
||||||
def visit_specification(self, children: Any) -> Typesdict:
|
def visit_specification(self, children: Any) -> Typesdict:
|
||||||
"""Process start symbol, return only children of modules."""
|
"""Process start symbol, return only children of modules."""
|
||||||
children = [x[0] for x in children if x is not None]
|
children = [x[0] for x in children if x is not None]
|
||||||
|
|||||||
@ -89,10 +89,11 @@ def normalize_msgtype(name: str) -> str:
|
|||||||
return str(path)
|
return str(path)
|
||||||
|
|
||||||
|
|
||||||
def normalize_fieldtype(field: Any, names: List[str]):
|
def normalize_fieldtype(typename: str, field: Any, names: List[str]):
|
||||||
"""Normalize field typename.
|
"""Normalize field typename.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
typename: Type name of field owner.
|
||||||
field: Field definition.
|
field: Field definition.
|
||||||
names: Valid message names.
|
names: Valid message names.
|
||||||
|
|
||||||
@ -111,9 +112,12 @@ def normalize_fieldtype(field: Any, names: List[str]):
|
|||||||
else:
|
else:
|
||||||
if name in dct:
|
if name in dct:
|
||||||
name = dct[name]
|
name = dct[name]
|
||||||
|
elif name == 'Header':
|
||||||
|
name = 'std_msgs/msg/Header'
|
||||||
|
elif '/' not in name:
|
||||||
|
name = str(Path(typename).parent / name)
|
||||||
elif '/msg/' not in name:
|
elif '/msg/' not in name:
|
||||||
ptype = Path(name)
|
name = str((path := Path(name)).parent / 'msg' / path.name)
|
||||||
name = str(ptype.parent / 'msg' / ptype.name)
|
|
||||||
inamedef = (Nodetype.NAME, name)
|
inamedef = (Nodetype.NAME, name)
|
||||||
|
|
||||||
if namedef[0] == Nodetype.NAME:
|
if namedef[0] == Nodetype.NAME:
|
||||||
@ -159,9 +163,9 @@ class VisitorMSG(Visitor):
|
|||||||
typelist = [children[0], *[x[1] for x in children[1]]]
|
typelist = [children[0], *[x[1] for x in children[1]]]
|
||||||
typedict = dict(typelist)
|
typedict = dict(typelist)
|
||||||
names = list(typedict.keys())
|
names = list(typedict.keys())
|
||||||
for _, fields in typedict.items():
|
for name, fields in typedict.items():
|
||||||
for field in fields:
|
for field in fields:
|
||||||
normalize_fieldtype(field, names)
|
normalize_fieldtype(name, field, names)
|
||||||
return typedict
|
return typedict
|
||||||
|
|
||||||
def visit_msgdef(self, children: Any) -> Any:
|
def visit_msgdef(self, children: Any) -> Any:
|
||||||
|
|||||||
@ -58,10 +58,22 @@ class Rule:
|
|||||||
class RuleLiteral(Rule):
|
class RuleLiteral(Rule):
|
||||||
"""Rule to match string literal."""
|
"""Rule to match string literal."""
|
||||||
|
|
||||||
|
def __init__(self, value: Any, rules: Dict[str, Rule], name: Optional[str] = None):
|
||||||
|
"""Initialize.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Value of this rule.
|
||||||
|
rules: Grammar containing all rules.
|
||||||
|
name: Name of this rule.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(value, rules, name)
|
||||||
|
self.value = value[1:-1].replace('\\\'', '\'')
|
||||||
|
|
||||||
def parse(self, text: str, pos: int) -> Tuple[int, Any]:
|
def parse(self, text: str, pos: int) -> Tuple[int, Any]:
|
||||||
"""Apply rule at position."""
|
"""Apply rule at position."""
|
||||||
value: str = self.value[1:-1].replace('\\\'', '\'')
|
value = self.value
|
||||||
if text[pos:].startswith(value):
|
if text[pos:pos + len(value)] == value:
|
||||||
npos = pos + len(value)
|
npos = pos + len(value)
|
||||||
npos = self.skip_ws(text, npos)
|
npos = self.skip_ws(text, npos)
|
||||||
return npos, (self.LIT, value)
|
return npos, (self.LIT, value)
|
||||||
@ -71,10 +83,21 @@ class RuleLiteral(Rule):
|
|||||||
class RuleRegex(Rule):
|
class RuleRegex(Rule):
|
||||||
"""Rule to match regular expression."""
|
"""Rule to match regular expression."""
|
||||||
|
|
||||||
|
def __init__(self, value: Any, rules: Dict[str, Rule], name: Optional[str] = None):
|
||||||
|
"""Initialize.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Value of this rule.
|
||||||
|
rules: Grammar containing all rules.
|
||||||
|
name: Name of this rule.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(value, rules, name)
|
||||||
|
self.value = re.compile(value[2:-1], re.M | re.S)
|
||||||
|
|
||||||
def parse(self, text: str, pos: int) -> Tuple[int, Any]:
|
def parse(self, text: str, pos: int) -> Tuple[int, Any]:
|
||||||
"""Apply rule at position."""
|
"""Apply rule at position."""
|
||||||
pattern = re.compile(self.value[2:-1], re.M | re.S)
|
match = self.value.match(text, pos)
|
||||||
match = pattern.match(text, pos)
|
|
||||||
if not match:
|
if not match:
|
||||||
return -1, []
|
return -1, []
|
||||||
npos = self.skip_ws(text, match.span()[1])
|
npos = self.skip_ws(text, match.span()[1])
|
||||||
@ -171,7 +194,6 @@ class Visitor: # pylint: disable=too-few-public-methods
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
self.typedefs = {}
|
|
||||||
|
|
||||||
def visit(self, tree: Any) -> Any:
|
def visit(self, tree: Any) -> Any:
|
||||||
"""Visit all nodes in parse tree."""
|
"""Visit all nodes in parse tree."""
|
||||||
|
|||||||
@ -37,6 +37,11 @@ MSG: test_msgs/Other
|
|||||||
uint64[3] Header
|
uint64[3] Header
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
RELSIBLING_MSG = """
|
||||||
|
Header header
|
||||||
|
Other other
|
||||||
|
"""
|
||||||
|
|
||||||
IDL_LANG = """
|
IDL_LANG = """
|
||||||
// assign different literals and expressions
|
// assign different literals and expressions
|
||||||
|
|
||||||
@ -122,6 +127,17 @@ def test_parse_multi_msg():
|
|||||||
assert ret['test_msgs/msg/Foo'][2][0][1] == 'uint8'
|
assert ret['test_msgs/msg/Foo'][2][0][1] == 'uint8'
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_relative_siblings_msg():
|
||||||
|
"""Test relative siblings with msg parser."""
|
||||||
|
ret = get_types_from_msg(RELSIBLING_MSG, 'test_msgs/msg/Foo')
|
||||||
|
assert ret['test_msgs/msg/Foo'][0][0][1] == 'std_msgs/msg/Header'
|
||||||
|
assert ret['test_msgs/msg/Foo'][1][0][1] == 'test_msgs/msg/Other'
|
||||||
|
|
||||||
|
ret = get_types_from_msg(RELSIBLING_MSG, 'rel_msgs/msg/Foo')
|
||||||
|
assert ret['rel_msgs/msg/Foo'][0][0][1] == 'std_msgs/msg/Header'
|
||||||
|
assert ret['rel_msgs/msg/Foo'][1][0][1] == 'rel_msgs/msg/Other'
|
||||||
|
|
||||||
|
|
||||||
def test_parse_idl():
|
def test_parse_idl():
|
||||||
"""Test idl parser."""
|
"""Test idl parser."""
|
||||||
ret = get_types_from_idl(IDL_LANG)
|
ret = get_types_from_idl(IDL_LANG)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user