idl-parser 0.2.2__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {idl_parser-0.2.2/idl_parser.egg-info → idl_parser-0.3.0}/PKG-INFO +2 -1
- {idl_parser-0.2.2 → idl_parser-0.3.0}/README.md +1 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/README.rst +1 -0
- idl_parser-0.3.0/examples/scoped_name_example.py +121 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/const.py +1 -1
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/enum.py +2 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/module.py +59 -20
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/node.py +41 -1
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/struct.py +3 -10
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/type.py +19 -2
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/typedef.py +8 -1
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/union.py +3 -4
- {idl_parser-0.2.2 → idl_parser-0.3.0/idl_parser.egg-info}/PKG-INFO +2 -1
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser.egg-info/SOURCES.txt +5 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser.egg-info/scm_file_list.json +5 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser.egg-info/scm_version.json +2 -2
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/examples_test.py +41 -1
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue30_test.py +1 -1
- idl_parser-0.3.0/tests/issue60_test.py +64 -0
- idl_parser-0.3.0/tests/issue67_test.py +117 -0
- idl_parser-0.3.0/tests/issue69_test.py +78 -0
- idl_parser-0.3.0/tests/issue72_test.py +143 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/.github/workflows/release.yml +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/.github/workflows/test.yml +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/.gitignore +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/.readthedocs.yaml +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/LICENSE +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/docs/api/index.rst +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/docs/conf.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/docs/index.rst +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/docs/requirements.txt +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/examples/_path.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/examples/annotation_example.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/examples/example.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/examples/union_example.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/__init__.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/bitmask.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/bitset.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/exception.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/interface.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/parser.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/pragma.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser/token_buffer.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser.egg-info/dependency_links.txt +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/idl_parser.egg-info/top_level.txt +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/pyproject.toml +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/setup.cfg +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/__init__.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/annotation_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/bitmask_bitset_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/forward_declaration_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/generalization_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/basic_module_test.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/circular/circular_a.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/circular/circular_b.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/circular/diamond.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/forward_declaration.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/generalization.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/generalization_extended.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/included_idl.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/including_idl.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/invalid_idl.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/issue12_subpath/relative_include.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/issue12_subpath/sensor_msgs/msg/Imu.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/issue12_subpath/std/msg/Header.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/issue18_idl42_int_types.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/issue28_pragma_keylist.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/issue9_const_pragma.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/issue9_tabs_bounded_string.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/issue9_whitespace_variants.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/idls/multi_module_test.idl +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/include_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/invalid_idl_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue12_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue18_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue31_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue39_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue43_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue45_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue48_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue49_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue50_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue52_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue53_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue57_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/issue9_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/key_annotation_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/module_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/multimodule_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/pragma_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/simple_dic_test.py +0 -0
- {idl_parser-0.2.2 → idl_parser-0.3.0}/tests/syntax_error_test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: idl_parser
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Very simple OMG IDL (Interface Definition Language) parser. This parses IDL files and outputs intermediate class objects.
|
|
5
5
|
Author-email: ysuga <ysuga@ysuga.net>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -102,6 +102,7 @@ More examples are in the ``examples`` folder of the repository:
|
|
|
102
102
|
- ``example.py``: interfaces, typedefs, unions and structs
|
|
103
103
|
- ``union_example.py``: unions (discriminator kinds, case and ``default`` labels, member types)
|
|
104
104
|
- ``annotation_example.py``: IDL 4 annotations (``@key``, ``@range``, ...) and struct keys
|
|
105
|
+
- ``scoped_name_example.py``: same-named types in several modules (relative ``X``, ``C::X`` and absolute ``::B::X`` names)
|
|
105
106
|
|
|
106
107
|
https://github.com/sugarsweetrobotics/idl_parser/tree/main/examples
|
|
107
108
|
|
|
@@ -77,6 +77,7 @@ More examples are in the [examples](examples/) folder:
|
|
|
77
77
|
| [`example.py`](examples/example.py) | interfaces, typedefs, unions and structs |
|
|
78
78
|
| [`union_example.py`](examples/union_example.py) | unions: discriminator kinds, case and `default` labels, member types |
|
|
79
79
|
| [`annotation_example.py`](examples/annotation_example.py) | IDL 4 annotations (`@key`, `@range`, ...) and struct keys |
|
|
80
|
+
| [`scoped_name_example.py`](examples/scoped_name_example.py) | same-named types in several modules: relative (`X`, `C::X`) and absolute (`::B::X`) names |
|
|
80
81
|
|
|
81
82
|
Run them with e.g. `python examples/union_example.py` (in a source checkout no install is needed).
|
|
82
83
|
|
|
@@ -79,6 +79,7 @@ More examples are in the ``examples`` folder of the repository:
|
|
|
79
79
|
- ``example.py``: interfaces, typedefs, unions and structs
|
|
80
80
|
- ``union_example.py``: unions (discriminator kinds, case and ``default`` labels, member types)
|
|
81
81
|
- ``annotation_example.py``: IDL 4 annotations (``@key``, ``@range``, ...) and struct keys
|
|
82
|
+
- ``scoped_name_example.py``: same-named types in several modules (relative ``X``, ``C::X`` and absolute ``::B::X`` names)
|
|
82
83
|
|
|
83
84
|
https://github.com/sugarsweetrobotics/idl_parser/tree/main/examples
|
|
84
85
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Example: resolve type names across modules (relative and absolute names).
|
|
2
|
+
|
|
3
|
+
When several modules define a type with the same name, a type name is
|
|
4
|
+
resolved with the IDL scoping rules (issues #69, #72):
|
|
5
|
+
|
|
6
|
+
- a relative name (``X``, ``C::X``) is looked up from the scope where it is
|
|
7
|
+
written, innermost first, then in the outer scopes;
|
|
8
|
+
- an absolute name (``::B::X``) is looked up from the global scope only.
|
|
9
|
+
|
|
10
|
+
Shows how to get
|
|
11
|
+
- the definition a member, argument or return type refers to (``obj`` / ``type``),
|
|
12
|
+
- the name as written (``ref_name``) and the name of the definition (``name``),
|
|
13
|
+
- a typedef resolved in the scope where it is defined,
|
|
14
|
+
- ``find_types()`` with and without a scope.
|
|
15
|
+
|
|
16
|
+
Run::
|
|
17
|
+
|
|
18
|
+
python examples/scoped_name_example.py
|
|
19
|
+
"""
|
|
20
|
+
import _path # noqa: F401 (use the idl_parser of this checkout)
|
|
21
|
+
|
|
22
|
+
from idl_parser import parser
|
|
23
|
+
|
|
24
|
+
IDL = '''
|
|
25
|
+
// A global Point, and a Point in each module
|
|
26
|
+
struct Point { long id; };
|
|
27
|
+
|
|
28
|
+
module geometry {
|
|
29
|
+
struct Point { double x; double y; };
|
|
30
|
+
typedef Point Origin; // geometry::Point (the scope of the typedef)
|
|
31
|
+
|
|
32
|
+
module d3 {
|
|
33
|
+
struct Point { double x; double y; double z; };
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module robot {
|
|
38
|
+
struct Point { long joint; };
|
|
39
|
+
|
|
40
|
+
module arm {
|
|
41
|
+
struct Point { double angle; };
|
|
42
|
+
|
|
43
|
+
interface Controller {
|
|
44
|
+
// relative names: looked up from robot::arm, then robot, then global
|
|
45
|
+
Point current(); // robot::arm::Point
|
|
46
|
+
void move_joint(in robot::Point p); // robot::Point
|
|
47
|
+
void move_to(in geometry::Point p); // geometry::Point
|
|
48
|
+
void move_to_3d(in geometry::d3::Point p);
|
|
49
|
+
// absolute names: from the global scope only
|
|
50
|
+
void mark(in ::Point p); // the global Point
|
|
51
|
+
void move_arm(in ::robot::arm::Point p);
|
|
52
|
+
geometry::Origin origin(); // typedef of geometry::Point
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
struct Pose {
|
|
57
|
+
Point joint; // robot::Point
|
|
58
|
+
arm::Point arm; // relative name with a scope: robot::arm::Point
|
|
59
|
+
sequence<geometry::Point> path; // element type is resolved too
|
|
60
|
+
::Point marker; // the global Point
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
'''
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def path(node):
|
|
67
|
+
"""Full path of a definition ('' for the global scope is shown as '::')."""
|
|
68
|
+
p = node.full_path.lstrip(':')
|
|
69
|
+
return p if '::' in p else '::' + p
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def show(label, written, definition):
|
|
73
|
+
print(' %-22s %-24s -> %s' % (label, written, path(definition)))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def main():
|
|
77
|
+
global_module = parser.IDLParser().load(IDL)
|
|
78
|
+
robot = global_module.module_by_name('robot')
|
|
79
|
+
arm = robot.module_by_name('arm')
|
|
80
|
+
|
|
81
|
+
print('robot::arm::Controller (name as written -> definition)')
|
|
82
|
+
controller = arm.interface_by_name('Controller')
|
|
83
|
+
for method in controller.methods:
|
|
84
|
+
if method.returns.name != 'void':
|
|
85
|
+
show(method.name + '() returns', method.returns.ref_name, method.returns.obj)
|
|
86
|
+
for a in method.arguments:
|
|
87
|
+
show(method.name + '(' + a.name + ')', a.type.ref_name, a.type.obj)
|
|
88
|
+
print()
|
|
89
|
+
|
|
90
|
+
# For a struct member, .type is the definition itself
|
|
91
|
+
print('robot::Pose members (member -> definition)')
|
|
92
|
+
pose = robot.struct_by_name('Pose')
|
|
93
|
+
for m in pose.members:
|
|
94
|
+
t = m.type
|
|
95
|
+
if t.is_sequence:
|
|
96
|
+
print(' %-22s -> sequence of %s' % (m.name, path(t.inner_type.obj)))
|
|
97
|
+
else:
|
|
98
|
+
print(' %-22s -> %s' % (m.name, path(t)))
|
|
99
|
+
print()
|
|
100
|
+
|
|
101
|
+
# The typedef is resolved where it is defined (module geometry),
|
|
102
|
+
# not where it is used (module robot::arm).
|
|
103
|
+
typedef = controller.method_by_name('origin').returns.obj
|
|
104
|
+
print('typedef %s -> %s' % (path(typedef), path(typedef.type)))
|
|
105
|
+
print()
|
|
106
|
+
|
|
107
|
+
# type.name is the name of the definition, without its scope
|
|
108
|
+
t = controller.method_by_name('move_to').arguments[0].type
|
|
109
|
+
print('name: %s, ref_name: %s, full path: %s' % (t.name, t.ref_name, path(t.obj)))
|
|
110
|
+
print()
|
|
111
|
+
|
|
112
|
+
# find_types(): with a scope the IDL scoping rules are used;
|
|
113
|
+
# without one, every definition with that name is returned.
|
|
114
|
+
print('find_types("Point", scope=robot::arm):', [path(n) for n in global_module.find_types('Point', scope=arm)])
|
|
115
|
+
print('find_types("Point", scope=robot): ', [path(n) for n in global_module.find_types('Point', scope=robot)])
|
|
116
|
+
print('find_types("::Point"): ', [path(n) for n in global_module.find_types('::Point')])
|
|
117
|
+
print('find_types("Point") (no scope): ', sorted(path(n) for n in global_module.find_types('Point')))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == '__main__':
|
|
121
|
+
main()
|
|
@@ -461,29 +461,68 @@ class IDLModule(node.IDLNode):
|
|
|
461
461
|
for m in self.typedefs:
|
|
462
462
|
retval.append(func(m))
|
|
463
463
|
|
|
464
|
-
def find_types(self, full_typename, parent=None):
|
|
464
|
+
def find_types(self, full_typename, parent=None, scope=None):
|
|
465
|
+
"""Return the type definitions named ``full_typename`` (a list).
|
|
466
|
+
|
|
467
|
+
``scope`` is the node where the name is written (issue #72). When it
|
|
468
|
+
is given, the name is resolved with the IDL scoping rules: it is
|
|
469
|
+
looked up in the innermost enclosing scope first, then in the outer
|
|
470
|
+
ones (``X`` in module ``B`` finds ``B::X`` before ``X`` elsewhere).
|
|
471
|
+
A name starting with ``::`` is looked up from the global scope only.
|
|
472
|
+
If the scoping rules find nothing, every definition whose name or
|
|
473
|
+
full path is ``full_typename`` is returned, as before.
|
|
474
|
+
"""
|
|
465
475
|
if type.is_primitive(full_typename):
|
|
466
476
|
return [type.IDLType(full_typename, self)]
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
typenode.append(s)
|
|
476
|
-
|
|
477
|
+
# A fully qualified name may start with '::' ("::M::A", issue #69).
|
|
478
|
+
# full_path has no leading '::' ("M::A"), so drop it before comparing.
|
|
479
|
+
name = str(full_typename).strip()
|
|
480
|
+
absolute = name.startswith('::')
|
|
481
|
+
if absolute:
|
|
482
|
+
name = name.lstrip(':').strip()
|
|
483
|
+
|
|
484
|
+
nodes = []
|
|
477
485
|
def parse_module(m):
|
|
478
486
|
m.for_each_module(parse_module)
|
|
479
|
-
m.for_each_struct
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
m.for_each_bitset(parse_node)
|
|
484
|
-
m.for_each_union(parse_node)
|
|
485
|
-
m.for_each_interface(parse_node)
|
|
486
|
-
|
|
487
|
+
for each in (m.for_each_struct, m.for_each_typedef, m.for_each_enum,
|
|
488
|
+
m.for_each_bitmask, m.for_each_bitset, m.for_each_union,
|
|
489
|
+
m.for_each_interface):
|
|
490
|
+
each(nodes.append)
|
|
487
491
|
parse_module(self)
|
|
488
492
|
|
|
489
|
-
|
|
493
|
+
def with_full_path(full):
|
|
494
|
+
return [s for s in nodes if s.full_path.lstrip(':') == full]
|
|
495
|
+
|
|
496
|
+
if absolute:
|
|
497
|
+
# '::X' always names X from the global scope
|
|
498
|
+
return with_full_path(name)
|
|
499
|
+
|
|
500
|
+
if scope is not None:
|
|
501
|
+
for prefix in _enclosing_scopes(scope):
|
|
502
|
+
found = with_full_path(prefix + sep + name if prefix else name)
|
|
503
|
+
if len(found) > 0:
|
|
504
|
+
return found
|
|
505
|
+
|
|
506
|
+
if parent:
|
|
507
|
+
prefix = parent.full_path.lstrip(':')
|
|
508
|
+
return [s for s in nodes
|
|
509
|
+
if s.full_path.lstrip(':') in ((prefix + sep + name if prefix else name), name)]
|
|
510
|
+
return [s for s in nodes if s.name == name or s.full_path.lstrip(':') == name]
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
_scope_classnames = ('IDLModule', 'IDLStruct', 'IDLUnion', 'IDLInterface')
|
|
514
|
+
|
|
515
|
+
def _enclosing_scopes(node):
|
|
516
|
+
"""Full paths (without a leading '::') of the scopes enclosing ``node``,
|
|
517
|
+
innermost first and ending with the global scope ('')."""
|
|
518
|
+
scopes = []
|
|
519
|
+
n = node
|
|
520
|
+
while n is not None:
|
|
521
|
+
if n.classname in _scope_classnames:
|
|
522
|
+
path = n.full_path.lstrip(':')
|
|
523
|
+
if path not in scopes:
|
|
524
|
+
scopes.append(path)
|
|
525
|
+
n = n.parent
|
|
526
|
+
if '' not in scopes:
|
|
527
|
+
scopes.append('')
|
|
528
|
+
return scopes
|
|
@@ -308,9 +308,49 @@ class IDLNode(object):
|
|
|
308
308
|
walk(self.root_node)
|
|
309
309
|
return len(found) > 0
|
|
310
310
|
|
|
311
|
+
def check_element_types(self, typ):
|
|
312
|
+
"""Raise InvalidDataTypeException if an element type of the sequence
|
|
313
|
+
or array ``typ`` (nested ones included) is not declared (issue #67).
|
|
314
|
+
|
|
315
|
+
A type that has been forward-declared ("interface A;", "struct A;",
|
|
316
|
+
"union A;") may be used before its definition, as with a direct member.
|
|
317
|
+
"""
|
|
318
|
+
from . import exception
|
|
319
|
+
while getattr(typ, 'is_sequence', False) or getattr(typ, 'is_array', False):
|
|
320
|
+
typ = typ.inner_type
|
|
321
|
+
if typ.classname != 'IDLBasicType':
|
|
322
|
+
continue
|
|
323
|
+
name = typ.name.strip()
|
|
324
|
+
if name.startswith(self.sep):
|
|
325
|
+
name = name[len(self.sep):]
|
|
326
|
+
if len(typ.resolve()) > 0:
|
|
327
|
+
continue
|
|
328
|
+
if self.is_pending_forward_declaration(name):
|
|
329
|
+
continue
|
|
330
|
+
if self._is_enclosing_definition(name):
|
|
331
|
+
continue # recursive type: "struct Node { sequence<Node> children; };"
|
|
332
|
+
print('Can not find Data Type (%s)\n' % typ.name)
|
|
333
|
+
raise exception.InvalidDataTypeException(
|
|
334
|
+
message='Can not find Data Type (%s)' % typ.name)
|
|
335
|
+
|
|
336
|
+
def _is_enclosing_definition(self, name):
|
|
337
|
+
"""True if name is a struct or union that encloses this node and is
|
|
338
|
+
still being parsed (so it is not registered in its module yet)."""
|
|
339
|
+
n = self.parent
|
|
340
|
+
while n is not None and not n.is_root:
|
|
341
|
+
if n.classname in ('IDLStruct', 'IDLUnion'):
|
|
342
|
+
if name == n.name or name == n.full_path.lstrip(':'):
|
|
343
|
+
return True
|
|
344
|
+
n = n.parent
|
|
345
|
+
return False
|
|
346
|
+
|
|
311
347
|
def refine_typename(self, typ):
|
|
312
348
|
return self._refine_typename(typ.name)
|
|
313
349
|
|
|
350
|
+
def _lookup_scope(self):
|
|
351
|
+
"""The node whose scope type names written here are resolved from."""
|
|
352
|
+
return self
|
|
353
|
+
|
|
314
354
|
def _refine_typename(self, name):
|
|
315
355
|
from . import type as idl_type
|
|
316
356
|
name = name.strip()
|
|
@@ -323,7 +363,7 @@ class IDLNode(object):
|
|
|
323
363
|
bounded = idl_type.parse_bounded_string(name)
|
|
324
364
|
if bounded is not None:
|
|
325
365
|
return bounded[0]
|
|
326
|
-
typs = self.root_node.find_types(name)
|
|
366
|
+
typs = self.root_node.find_types(name, scope=self._lookup_scope())
|
|
327
367
|
if len(typs) == 0:
|
|
328
368
|
# Not resolvable yet (e.g. a forward-declared interface): keep the name
|
|
329
369
|
return name
|
|
@@ -41,8 +41,6 @@ class IDLMember(node.IDLNode):
|
|
|
41
41
|
if recursive:
|
|
42
42
|
if self.type.is_primitive:
|
|
43
43
|
return str(self.type) + ' ' + self.name
|
|
44
|
-
elif self.type.obj.is_enum:
|
|
45
|
-
return str('enum') + ' ' + self.name
|
|
46
44
|
dic = {str(self.type) +' ' + self.name :
|
|
47
45
|
self.type.obj.to_simple_dic(recursive=recursive, member_only=True)}
|
|
48
46
|
return dic
|
|
@@ -59,16 +57,10 @@ class IDLMember(node.IDLNode):
|
|
|
59
57
|
@property
|
|
60
58
|
def type(self):
|
|
61
59
|
if self._type.classname == 'IDLBasicType': # Struct
|
|
62
|
-
typs = self.
|
|
60
|
+
typs = self._type.resolve() # from this member's scope (issue #72)
|
|
63
61
|
if len(typs) == 0:
|
|
64
|
-
print('Can not find Data Type (%s)\n' % self._type.
|
|
62
|
+
print('Can not find Data Type (%s)\n' % self._type.ref_name)
|
|
65
63
|
raise exception.InvalidDataTypeException()
|
|
66
|
-
elif len(typs) > 1:
|
|
67
|
-
typs = self.root_node.find_types(self._type.name, parent=self.parent.parent)
|
|
68
|
-
if len(typs) == 0:
|
|
69
|
-
print('Can not find Data Type (%s)\n' % self._type.name)
|
|
70
|
-
raise exception.InvalidDataTypeException()
|
|
71
|
-
|
|
72
64
|
return typs[0]
|
|
73
65
|
return self._type
|
|
74
66
|
|
|
@@ -83,6 +75,7 @@ class IDLMember(node.IDLNode):
|
|
|
83
75
|
def post_process(self):
|
|
84
76
|
if self._type.classname == 'IDLBasicType' and self.is_pending_forward_declaration(self._type.name):
|
|
85
77
|
return # forward-declared type; resolved via .type once it is defined
|
|
78
|
+
self.check_element_types(self._type) # sequence / array elements (issue #67)
|
|
86
79
|
self._type._name = self.refine_typename(self.type)
|
|
87
80
|
|
|
88
81
|
|
|
@@ -383,16 +383,33 @@ class IDLPrimitive(IDLTypeBase):
|
|
|
383
383
|
return (self.parent.full_path + self.sep + self.name).strip()
|
|
384
384
|
class IDLBasicType(IDLTypeBase):
|
|
385
385
|
def __init__(self, name, parent):
|
|
386
|
+
# The node where the name is written: the name is resolved from its
|
|
387
|
+
# scope (issue #72). The parent of a type node is the root node.
|
|
388
|
+
self._scope = parent
|
|
389
|
+
self._ref_name = name.strip()
|
|
386
390
|
super(IDLBasicType, self).__init__('IDLBasicType', name, parent.root_node)
|
|
387
391
|
self._verbose = True
|
|
388
392
|
#if self.name.find('['):
|
|
389
393
|
# self._name = self.name[self.name.find('[')+1:]
|
|
390
394
|
self._name = self.refine_typename(self)
|
|
391
395
|
|
|
396
|
+
@property
|
|
397
|
+
def ref_name(self):
|
|
398
|
+
"""The type name as written (``'A::X'``, ``'::B::X'``). ``name`` is
|
|
399
|
+
the name of the definition without its scope (``'X'``)."""
|
|
400
|
+
return self._ref_name
|
|
401
|
+
|
|
402
|
+
def _lookup_scope(self):
|
|
403
|
+
return self._scope
|
|
404
|
+
|
|
405
|
+
def resolve(self):
|
|
406
|
+
"""The definitions this name refers to, found from the scope where
|
|
407
|
+
it is written (a list; empty if it is not defined)."""
|
|
408
|
+
return self.root_node.find_types(self._ref_name, scope=self._scope)
|
|
409
|
+
|
|
392
410
|
@property
|
|
393
411
|
def obj(self):
|
|
394
|
-
|
|
395
|
-
typs = global_module.find_types(self.name)
|
|
412
|
+
typs = self.resolve()
|
|
396
413
|
if len(typs) == 0:
|
|
397
414
|
return None
|
|
398
415
|
else:
|
|
@@ -40,7 +40,13 @@ class IDLTypedef(node.IDLNode):
|
|
|
40
40
|
@property
|
|
41
41
|
def type(self):
|
|
42
42
|
if self._type.classname == 'IDLBasicType': # Struct
|
|
43
|
-
|
|
43
|
+
typs = self._type.resolve() # from this typedef's scope (issue #72)
|
|
44
|
+
if len(typs) == 0:
|
|
45
|
+
# issue #69: report an unknown type instead of an IndexError
|
|
46
|
+
from . import exception
|
|
47
|
+
raise exception.InvalidDataTypeException(
|
|
48
|
+
message='Can not find Data Type (%s)' % self._type.ref_name)
|
|
49
|
+
return typs[0]
|
|
44
50
|
return self._type
|
|
45
51
|
|
|
46
52
|
def get_type(self, extract_typedef=False):
|
|
@@ -83,4 +89,5 @@ class IDLTypedef(node.IDLNode):
|
|
|
83
89
|
def _post_process(self):
|
|
84
90
|
if self._type.classname == 'IDLBasicType' and self.is_pending_forward_declaration(self._type.name):
|
|
85
91
|
return # forward-declared type; resolved via .type once it is defined
|
|
92
|
+
self.check_element_types(self._type) # sequence / array elements (issue #67)
|
|
86
93
|
self._type._name = self.refine_typename(self.type)
|
|
@@ -52,8 +52,6 @@ class IDLUnionMember(node.IDLNode):
|
|
|
52
52
|
if recursive:
|
|
53
53
|
if self.type.is_primitive:
|
|
54
54
|
return str(self.type) + ' ' + self.name
|
|
55
|
-
elif self.type.obj.is_enum:
|
|
56
|
-
return str('enum') + ' ' + self.name
|
|
57
55
|
dic = {str(self.type) +' ' + self.name :
|
|
58
56
|
self.type.obj.to_simple_dic(recursive=recursive, member_only=True)}
|
|
59
57
|
return dic
|
|
@@ -72,9 +70,9 @@ class IDLUnionMember(node.IDLNode):
|
|
|
72
70
|
@property
|
|
73
71
|
def type(self):
|
|
74
72
|
if self._type.classname == 'IDLBasicType': # Union
|
|
75
|
-
typs = self.
|
|
73
|
+
typs = self._type.resolve() # from this member's scope (issue #72)
|
|
76
74
|
if len(typs) == 0:
|
|
77
|
-
print('Can not find Data Type (%s)\n' % self._type.
|
|
75
|
+
print('Can not find Data Type (%s)\n' % self._type.ref_name)
|
|
78
76
|
raise exception.InvalidDataTypeException()
|
|
79
77
|
return typs[0]
|
|
80
78
|
return self._type
|
|
@@ -98,6 +96,7 @@ class IDLUnionMember(node.IDLNode):
|
|
|
98
96
|
def post_process(self):
|
|
99
97
|
if self._type.classname == 'IDLBasicType' and self.is_pending_forward_declaration(self._type.name):
|
|
100
98
|
return # forward-declared type; resolved via .type once it is defined
|
|
99
|
+
self.check_element_types(self._type) # sequence / array elements (issue #67)
|
|
101
100
|
self._type._name = self.refine_typename(self.type)
|
|
102
101
|
|
|
103
102
|
class IDLUnion(node.IDLNode):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: idl_parser
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Very simple OMG IDL (Interface Definition Language) parser. This parses IDL files and outputs intermediate class objects.
|
|
5
5
|
Author-email: ysuga <ysuga@ysuga.net>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -102,6 +102,7 @@ More examples are in the ``examples`` folder of the repository:
|
|
|
102
102
|
- ``example.py``: interfaces, typedefs, unions and structs
|
|
103
103
|
- ``union_example.py``: unions (discriminator kinds, case and ``default`` labels, member types)
|
|
104
104
|
- ``annotation_example.py``: IDL 4 annotations (``@key``, ``@range``, ...) and struct keys
|
|
105
|
+
- ``scoped_name_example.py``: same-named types in several modules (relative ``X``, ``C::X`` and absolute ``::B::X`` names)
|
|
105
106
|
|
|
106
107
|
https://github.com/sugarsweetrobotics/idl_parser/tree/main/examples
|
|
107
108
|
|
|
@@ -13,6 +13,7 @@ docs/api/index.rst
|
|
|
13
13
|
examples/_path.py
|
|
14
14
|
examples/annotation_example.py
|
|
15
15
|
examples/example.py
|
|
16
|
+
examples/scoped_name_example.py
|
|
16
17
|
examples/union_example.py
|
|
17
18
|
idl_parser/__init__.py
|
|
18
19
|
idl_parser/bitmask.py
|
|
@@ -57,6 +58,10 @@ tests/issue50_test.py
|
|
|
57
58
|
tests/issue52_test.py
|
|
58
59
|
tests/issue53_test.py
|
|
59
60
|
tests/issue57_test.py
|
|
61
|
+
tests/issue60_test.py
|
|
62
|
+
tests/issue67_test.py
|
|
63
|
+
tests/issue69_test.py
|
|
64
|
+
tests/issue72_test.py
|
|
60
65
|
tests/issue9_test.py
|
|
61
66
|
tests/key_annotation_test.py
|
|
62
67
|
tests/module_test.py
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"examples/_path.py",
|
|
15
15
|
"examples/annotation_example.py",
|
|
16
16
|
"examples/example.py",
|
|
17
|
+
"examples/scoped_name_example.py",
|
|
17
18
|
"examples/union_example.py",
|
|
18
19
|
"idl_parser/__init__.py",
|
|
19
20
|
"idl_parser/bitmask.py",
|
|
@@ -72,6 +73,10 @@
|
|
|
72
73
|
"tests/issue52_test.py",
|
|
73
74
|
"tests/issue53_test.py",
|
|
74
75
|
"tests/issue57_test.py",
|
|
76
|
+
"tests/issue60_test.py",
|
|
77
|
+
"tests/issue67_test.py",
|
|
78
|
+
"tests/issue69_test.py",
|
|
79
|
+
"tests/issue72_test.py",
|
|
75
80
|
"tests/issue9_test.py",
|
|
76
81
|
"tests/key_annotation_test.py",
|
|
77
82
|
"tests/module_test.py",
|
|
@@ -50,7 +50,8 @@ class ExamplesRunAsScriptTest(unittest.TestCase):
|
|
|
50
50
|
|
|
51
51
|
def test_examples_exist(self):
|
|
52
52
|
names = [os.path.basename(p) for p in example_scripts()]
|
|
53
|
-
for name in ('example.py', 'union_example.py', 'annotation_example.py'
|
|
53
|
+
for name in ('example.py', 'union_example.py', 'annotation_example.py',
|
|
54
|
+
'scoped_name_example.py'):
|
|
54
55
|
self.assertIn(name, names)
|
|
55
56
|
|
|
56
57
|
def test_each_example_runs_as_script(self):
|
|
@@ -174,5 +175,44 @@ class AnnotationExampleTest(unittest.TestCase):
|
|
|
174
175
|
self.assertIn('"APPENDABLE"', self.text)
|
|
175
176
|
|
|
176
177
|
|
|
178
|
+
class ScopedNameExampleTest(unittest.TestCase):
|
|
179
|
+
|
|
180
|
+
@classmethod
|
|
181
|
+
def setUpClass(cls):
|
|
182
|
+
cls.text = run_main('scoped_name_example')
|
|
183
|
+
|
|
184
|
+
def assertResolved(self, label, written, definition):
|
|
185
|
+
for line in self.text.splitlines():
|
|
186
|
+
if line.split() == [*label.split(), written, '->', definition]:
|
|
187
|
+
return
|
|
188
|
+
self.fail('no line for %s %s -> %s in output:\n%s' % (label, written, definition, self.text))
|
|
189
|
+
|
|
190
|
+
def test_relative_names(self):
|
|
191
|
+
self.assertResolved('current() returns', 'Point', 'robot::arm::Point')
|
|
192
|
+
self.assertResolved('move_joint(p)', 'robot::Point', 'robot::Point')
|
|
193
|
+
self.assertResolved('move_to(p)', 'geometry::Point', 'geometry::Point')
|
|
194
|
+
self.assertResolved('move_to_3d(p)', 'geometry::d3::Point', 'geometry::d3::Point')
|
|
195
|
+
|
|
196
|
+
def test_absolute_names(self):
|
|
197
|
+
self.assertResolved('mark(p)', '::Point', '::Point')
|
|
198
|
+
self.assertResolved('move_arm(p)', '::robot::arm::Point', 'robot::arm::Point')
|
|
199
|
+
|
|
200
|
+
def test_struct_members(self):
|
|
201
|
+
self.assertIn(' joint -> robot::Point', self.text)
|
|
202
|
+
self.assertIn(' arm -> robot::arm::Point', self.text)
|
|
203
|
+
self.assertIn(' path -> sequence of geometry::Point', self.text)
|
|
204
|
+
self.assertIn(' marker -> ::Point', self.text)
|
|
205
|
+
|
|
206
|
+
def test_typedef_and_names(self):
|
|
207
|
+
self.assertIn('typedef geometry::Origin -> geometry::Point', self.text)
|
|
208
|
+
self.assertIn('name: Point, ref_name: geometry::Point, full path: geometry::Point', self.text)
|
|
209
|
+
|
|
210
|
+
def test_find_types(self):
|
|
211
|
+
self.assertIn("scope=robot::arm): ['robot::arm::Point']", self.text)
|
|
212
|
+
self.assertIn("scope=robot): ['robot::Point']", self.text)
|
|
213
|
+
self.assertIn("(no scope): ['::Point', 'geometry::Point', 'geometry::d3::Point', "
|
|
214
|
+
"'robot::Point', 'robot::arm::Point']", self.text)
|
|
215
|
+
|
|
216
|
+
|
|
177
217
|
if __name__ == '__main__':
|
|
178
218
|
unittest.main()
|
|
@@ -68,7 +68,7 @@ class MemberTypeSimpleDicTest(unittest.TestCase):
|
|
|
68
68
|
'WithSeqInline': [{'sequence<long> q': {'sequence<long>': 'long'}}],
|
|
69
69
|
'WithArray': [{'long[3] a': 'long[3]'}],
|
|
70
70
|
'WithStruct': [{'T t': ['long s']}],
|
|
71
|
-
'WithEnum': ['
|
|
71
|
+
'WithEnum': [{'E e': [{'A': 0}, {'B': 1}]}],
|
|
72
72
|
'WithBitmask': [{'F f': [{'F1': 0}, {'F2': 1}]}],
|
|
73
73
|
'WithBitset': [{'BS bs': [{'b': 'bitfield<3, octet>'}]}],
|
|
74
74
|
'WithSeqTypedef': [{'DSeq d': {'typedef sequence<double> DSeq':
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""to_simple_dic(recursive=True) expands enum members like other types (issue #60).
|
|
2
|
+
|
|
3
|
+
A direct enum member used to become ``'enum <member name>'`` (no type name,
|
|
4
|
+
no values), while ``sequence<E>`` / typedef of an enum expanded the values.
|
|
5
|
+
Enum members now expand the same way as struct and bitmask members.
|
|
6
|
+
"""
|
|
7
|
+
import unittest
|
|
8
|
+
|
|
9
|
+
from idl_parser import parser
|
|
10
|
+
|
|
11
|
+
IDL = '''
|
|
12
|
+
module M {
|
|
13
|
+
enum E { A, B };
|
|
14
|
+
bitmask F { F1, F2 };
|
|
15
|
+
typedef E EAlias;
|
|
16
|
+
typedef sequence<E> ESeq;
|
|
17
|
+
struct S { E e; sequence<E> se; EAlias ea; ESeq es; F f; };
|
|
18
|
+
union U switch (E) { case A: E ue; case B: long l; };
|
|
19
|
+
};
|
|
20
|
+
'''
|
|
21
|
+
|
|
22
|
+
VALUES = [{'A': 0}, {'B': 1}]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class EnumMemberSimpleDicTest(unittest.TestCase):
|
|
26
|
+
|
|
27
|
+
def setUp(self):
|
|
28
|
+
self.m = parser.IDLParser().load(IDL).module_by_name('M')
|
|
29
|
+
|
|
30
|
+
def _members(self):
|
|
31
|
+
return self.m.struct_by_name('S').to_simple_dic(recursive=True)['struct S']
|
|
32
|
+
|
|
33
|
+
def test_direct_member(self):
|
|
34
|
+
self.assertEqual(self._members()[0], {'E e': VALUES})
|
|
35
|
+
|
|
36
|
+
def test_sequence_of_enum(self):
|
|
37
|
+
self.assertEqual(self._members()[1], {'sequence<E> se': {'sequence<E>': VALUES}})
|
|
38
|
+
|
|
39
|
+
def test_typedef_of_enum(self):
|
|
40
|
+
self.assertEqual(self._members()[2], {'EAlias ea': {'typedef E EAlias': VALUES}})
|
|
41
|
+
|
|
42
|
+
def test_typedef_of_sequence_of_enum(self):
|
|
43
|
+
self.assertEqual(self._members()[3],
|
|
44
|
+
{'ESeq es': {'typedef sequence<E> ESeq': {'sequence<E>': VALUES}}})
|
|
45
|
+
|
|
46
|
+
def test_same_shape_as_bitmask(self):
|
|
47
|
+
self.assertEqual(self._members()[4], {'F f': [{'F1': 0}, {'F2': 1}]})
|
|
48
|
+
|
|
49
|
+
def test_union_member(self):
|
|
50
|
+
self.assertEqual(self.m.union_by_name('U').to_simple_dic(recursive=True),
|
|
51
|
+
{'union U': [{'E ue': VALUES}, 'long l']})
|
|
52
|
+
|
|
53
|
+
def test_enum_itself_unchanged(self):
|
|
54
|
+
e = self.m.enum_by_name('E')
|
|
55
|
+
self.assertEqual(e.to_simple_dic(), {'enum E': VALUES})
|
|
56
|
+
self.assertEqual(e.to_simple_dic(member_only=True), VALUES)
|
|
57
|
+
self.assertEqual(e.to_simple_dic(quiet=True), 'enum E')
|
|
58
|
+
|
|
59
|
+
def test_non_recursive_unchanged(self):
|
|
60
|
+
self.assertEqual(self.m.struct_by_name('S').to_simple_dic()['struct S'][0], {'e': 'E'})
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == '__main__':
|
|
64
|
+
unittest.main()
|