idl-parser 0.2.0__tar.gz → 0.2.1__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.0/idl_parser.egg-info → idl_parser-0.2.1}/PKG-INFO +9 -1
- {idl_parser-0.2.0 → idl_parser-0.2.1}/README.md +10 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/README.rst +8 -0
- idl_parser-0.2.1/examples/_path.py +14 -0
- idl_parser-0.2.1/examples/annotation_example.py +160 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1/examples}/example.py +14 -4
- idl_parser-0.2.1/examples/union_example.py +143 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/module.py +16 -6
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/parser.py +84 -34
- idl_parser-0.2.1/idl_parser/token_buffer.py +109 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1/idl_parser.egg-info}/PKG-INFO +9 -1
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser.egg-info/SOURCES.txt +10 -2
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser.egg-info/scm_file_list.json +10 -2
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser.egg-info/scm_version.json +2 -2
- idl_parser-0.2.1/tests/examples_test.py +171 -0
- idl_parser-0.2.1/tests/issue49_test.py +80 -0
- idl_parser-0.2.1/tests/issue50_test.py +118 -0
- idl_parser-0.2.1/tests/issue52_test.py +122 -0
- idl_parser-0.2.1/tests/issue53_test.py +134 -0
- idl_parser-0.2.0/tests/example_test.py → idl_parser-0.2.1/tests/simple_dic_test.py +4 -18
- idl_parser-0.2.0/idl_parser/token_buffer.py +0 -29
- {idl_parser-0.2.0 → idl_parser-0.2.1}/.github/workflows/release.yml +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/.github/workflows/test.yml +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/.gitignore +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/.readthedocs.yaml +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/docs/api/index.rst +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/docs/conf.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/docs/index.rst +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/docs/requirements.txt +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/__init__.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/bitmask.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/bitset.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/const.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/enum.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/exception.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/interface.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/node.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/pragma.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/struct.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/type.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/typedef.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser/union.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser.egg-info/dependency_links.txt +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idl_parser.egg-info/top_level.txt +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/basic_module_test.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/circular/circular_a.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/circular/circular_b.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/circular/diamond.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/forward_declaration.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/generalization.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/generalization_extended.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/included_idl.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/including_idl.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/invalid_idl.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/issue12_subpath/relative_include.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/issue12_subpath/sensor_msgs/msg/Imu.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/issue12_subpath/std/msg/Header.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/issue18_idl42_int_types.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/issue28_pragma_keylist.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/issue9_const_pragma.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/issue9_tabs_bounded_string.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/issue9_whitespace_variants.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/idls/multi_module_test.idl +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/pyproject.toml +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/setup.cfg +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/__init__.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/annotation_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/bitmask_bitset_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/forward_declaration_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/generalization_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/include_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/invalid_idl_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/issue12_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/issue18_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/issue39_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/issue9_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/key_annotation_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/module_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/multimodule_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/tests/pragma_test.py +0 -0
- {idl_parser-0.2.0 → idl_parser-0.2.1}/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.2.
|
|
3
|
+
Version: 0.2.1
|
|
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
|
Project-URL: Homepage, http://www.sugarsweetrobotics.com/
|
|
@@ -95,6 +95,14 @@ Example
|
|
|
95
95
|
print(' name:', m.name)
|
|
96
96
|
print(' type:', m.type.name)
|
|
97
97
|
|
|
98
|
+
More examples are in the ``examples`` folder of the repository:
|
|
99
|
+
|
|
100
|
+
- ``example.py``: interfaces, typedefs, unions and structs
|
|
101
|
+
- ``union_example.py``: unions (discriminator kinds, case labels, member types)
|
|
102
|
+
- ``annotation_example.py``: IDL 4 annotations (``@key``, ``@range``, ...) and struct keys
|
|
103
|
+
|
|
104
|
+
https://github.com/sugarsweetrobotics/idl_parser/tree/main/examples
|
|
105
|
+
|
|
98
106
|
How to install
|
|
99
107
|
---------------
|
|
100
108
|
|
|
@@ -70,6 +70,16 @@ for m in timedDoubleSeq.members:
|
|
|
70
70
|
print(' name:', m.name)
|
|
71
71
|
print(' type:', m.type.name)
|
|
72
72
|
```
|
|
73
|
+
More examples are in the [examples](examples/) folder:
|
|
74
|
+
|
|
75
|
+
| File | Shows |
|
|
76
|
+
|---|---|
|
|
77
|
+
| [`example.py`](examples/example.py) | interfaces, typedefs, unions and structs |
|
|
78
|
+
| [`union_example.py`](examples/union_example.py) | unions: discriminator kinds, case labels, member types |
|
|
79
|
+
| [`annotation_example.py`](examples/annotation_example.py) | IDL 4 annotations (`@key`, `@range`, ...) and struct keys |
|
|
80
|
+
|
|
81
|
+
Run them with e.g. `python examples/union_example.py` (in a source checkout no install is needed).
|
|
82
|
+
|
|
73
83
|
## How to install
|
|
74
84
|
sudo pip install idl_parser
|
|
75
85
|
|
|
@@ -74,6 +74,14 @@ Example
|
|
|
74
74
|
print(' name:', m.name)
|
|
75
75
|
print(' type:', m.type.name)
|
|
76
76
|
|
|
77
|
+
More examples are in the ``examples`` folder of the repository:
|
|
78
|
+
|
|
79
|
+
- ``example.py``: interfaces, typedefs, unions and structs
|
|
80
|
+
- ``union_example.py``: unions (discriminator kinds, case labels, member types)
|
|
81
|
+
- ``annotation_example.py``: IDL 4 annotations (``@key``, ``@range``, ...) and struct keys
|
|
82
|
+
|
|
83
|
+
https://github.com/sugarsweetrobotics/idl_parser/tree/main/examples
|
|
84
|
+
|
|
77
85
|
How to install
|
|
78
86
|
---------------
|
|
79
87
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Make the examples use the idl_parser of this source checkout.
|
|
2
|
+
|
|
3
|
+
``python examples/xxx.py`` puts ``examples/`` (not the repository root) on
|
|
4
|
+
``sys.path``, so ``import idl_parser`` would fail without installing the
|
|
5
|
+
package, or would pick up an older installed version. When the examples sit
|
|
6
|
+
in a checkout next to the ``idl_parser`` package, that package is used.
|
|
7
|
+
Copied elsewhere, the examples use the installed ``idl_parser``.
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
13
|
+
if os.path.isdir(os.path.join(_root, 'idl_parser')) and _root not in sys.path:
|
|
14
|
+
sys.path.insert(0, _root)
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Example: read IDL 4 annotations (``@key``, ``@range(min=0, max=10)``, ...).
|
|
2
|
+
|
|
3
|
+
Annotations are kept on whatever they are written before: a module, struct,
|
|
4
|
+
union, enum, typedef, const, interface, struct/union member, enum value,
|
|
5
|
+
method or method argument. They never leak into type names.
|
|
6
|
+
|
|
7
|
+
Shows how to
|
|
8
|
+
- list annotations and look one up by name (``annotations``,
|
|
9
|
+
``annotation_by_name``, ``has_annotation``),
|
|
10
|
+
- read annotation arguments (``args``, ``params``, ``value``),
|
|
11
|
+
- get struct keys from ``@key`` (combined with ``#pragma keylist``),
|
|
12
|
+
- read annotations that change parsing (``@bit_bound``, ``@position``),
|
|
13
|
+
- get annotations in ``to_dic()`` output,
|
|
14
|
+
- use your own annotations (``@annotation`` definitions are skipped).
|
|
15
|
+
|
|
16
|
+
Run::
|
|
17
|
+
|
|
18
|
+
python examples/annotation_example.py
|
|
19
|
+
"""
|
|
20
|
+
import json
|
|
21
|
+
|
|
22
|
+
import _path # noqa: F401 (use the idl_parser of this checkout)
|
|
23
|
+
|
|
24
|
+
from idl_parser import parser
|
|
25
|
+
|
|
26
|
+
IDL = '''
|
|
27
|
+
// A user-defined annotation. The definition itself is skipped by the parser,
|
|
28
|
+
// but uses of it (@unit_system(...)) are kept like any other annotation.
|
|
29
|
+
@annotation unit_system { string name default "SI"; };
|
|
30
|
+
|
|
31
|
+
@unit_system(name="SI")
|
|
32
|
+
module robot {
|
|
33
|
+
|
|
34
|
+
@extensibility(APPENDABLE) @topic
|
|
35
|
+
struct JointState {
|
|
36
|
+
@key long joint_id;
|
|
37
|
+
@key string robot_name;
|
|
38
|
+
@range(min=-180, max=180) @unit("deg") double position;
|
|
39
|
+
@unit("deg/s") @default(0) double velocity;
|
|
40
|
+
@optional double effort;
|
|
41
|
+
long sequence_number;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
struct Command {
|
|
45
|
+
long target_id;
|
|
46
|
+
long priority;
|
|
47
|
+
@key long command_id;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
enum Mode {
|
|
51
|
+
@value(10) IDLE,
|
|
52
|
+
@value(20) RUNNING,
|
|
53
|
+
@value(99) @deprecated ERROR
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
@extensibility(FINAL)
|
|
57
|
+
union Setpoint switch (Mode) {
|
|
58
|
+
case IDLE:
|
|
59
|
+
@unit("deg") double hold_position;
|
|
60
|
+
case RUNNING:
|
|
61
|
+
@unit("deg/s") double target_velocity;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
@bit_bound(8)
|
|
65
|
+
bitmask Status {
|
|
66
|
+
@position(0) POWERED,
|
|
67
|
+
MOVING,
|
|
68
|
+
@position(7) FAULT
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
@range(min=0, max=100) typedef long Percent;
|
|
72
|
+
|
|
73
|
+
@verbatim(language="c++", text="constexpr") const long MAX_JOINTS = 7;
|
|
74
|
+
|
|
75
|
+
@service(platform="DDS")
|
|
76
|
+
interface Controller {
|
|
77
|
+
@oneway_hint void setMode(in Mode mode);
|
|
78
|
+
long move(@range(min=0, max=6) in long joint_id, in double position);
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
#pragma keylist robot::Command target_id
|
|
83
|
+
'''
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def show(label, node):
|
|
87
|
+
anns = ' '.join(str(a) for a in node.annotations) or '(none)'
|
|
88
|
+
print('%-28s %s' % (label, anns))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def main():
|
|
92
|
+
global_module = parser.IDLParser().load(IDL)
|
|
93
|
+
robot = global_module.module_by_name('robot')
|
|
94
|
+
|
|
95
|
+
print('== Annotations on each kind of definition')
|
|
96
|
+
show('module robot', robot)
|
|
97
|
+
joint_state = robot.struct_by_name('JointState')
|
|
98
|
+
show('struct JointState', joint_state)
|
|
99
|
+
for m in joint_state.members:
|
|
100
|
+
# Annotations never become part of the type name.
|
|
101
|
+
show(' %s %s' % (m.type, m.name), m)
|
|
102
|
+
mode = robot.enum_by_name('Mode')
|
|
103
|
+
for v in mode.values:
|
|
104
|
+
show('enum value %s' % v.name, v)
|
|
105
|
+
setpoint = robot.union_by_name('Setpoint')
|
|
106
|
+
show('union Setpoint', setpoint)
|
|
107
|
+
for m in setpoint.members:
|
|
108
|
+
show(' case %s: %s' % (','.join(m.descriminator_value_associations), m.name), m)
|
|
109
|
+
show('typedef Percent', robot.typedef_by_name('Percent'))
|
|
110
|
+
show('const MAX_JOINTS', robot.const_by_name('MAX_JOINTS'))
|
|
111
|
+
controller = robot.interface_by_name('Controller')
|
|
112
|
+
show('interface Controller', controller)
|
|
113
|
+
for method in controller.methods:
|
|
114
|
+
show(' method %s' % method.name, method)
|
|
115
|
+
for a in method.arguments:
|
|
116
|
+
show(' arg %s' % a.name, a)
|
|
117
|
+
print()
|
|
118
|
+
|
|
119
|
+
print('== Reading annotation arguments')
|
|
120
|
+
position = joint_state.member_by_name('position')
|
|
121
|
+
rng = position.annotation_by_name('range')
|
|
122
|
+
print('str: ', str(rng)) # @range(min=-180, max=180)
|
|
123
|
+
print('name: ', rng.name) # range
|
|
124
|
+
print('params: ', rng.params) # {'min': '-180', 'max': '180'}
|
|
125
|
+
print('min/max:', int(rng.params['min']), int(rng.params['max']))
|
|
126
|
+
unit = position.annotation_by_name('unit')
|
|
127
|
+
print('unit: ', unit.args, unit.value) # ['"deg"'] "deg" (quotes are kept)
|
|
128
|
+
print('unit without quotes:', unit.value.strip('"'))
|
|
129
|
+
print('IDLE value:', int(mode.value_by_name('IDLE').annotation_by_name('value').value))
|
|
130
|
+
print('ERROR deprecated?', mode.value_by_name('ERROR').has_annotation('deprecated'))
|
|
131
|
+
print('effort optional?', joint_state.member_by_name('effort').has_annotation('optional'))
|
|
132
|
+
print('sequence_number annotation:', joint_state.member_by_name('sequence_number').annotation_by_name('range'))
|
|
133
|
+
print('module unit system:', robot.annotation_by_name('unit_system').params['name'])
|
|
134
|
+
print()
|
|
135
|
+
|
|
136
|
+
print('== Keys from @key and #pragma keylist')
|
|
137
|
+
print('JointState keys:', joint_state.keys)
|
|
138
|
+
print('JointState key members:', [m.name for m in joint_state.members if m.is_key])
|
|
139
|
+
command = robot.struct_by_name('Command')
|
|
140
|
+
# Keys from #pragma keylist come first, then members marked @key.
|
|
141
|
+
print('Command keys:', command.keys,
|
|
142
|
+
'(pragma: %s, @key: %s)' % (command.pragma_keys, command.annotated_keys))
|
|
143
|
+
print()
|
|
144
|
+
|
|
145
|
+
print('== Annotations that change parsing')
|
|
146
|
+
status = robot.bitmask_by_name('Status')
|
|
147
|
+
print('Status bit_bound:', status.bit_bound)
|
|
148
|
+
print('Status positions:', [(v.name, v.position) for v in status.values])
|
|
149
|
+
print()
|
|
150
|
+
|
|
151
|
+
print('== to_dic() includes annotations')
|
|
152
|
+
dic = robot.struct_by_name('JointState').to_dic()
|
|
153
|
+
print(json.dumps({'annotations': dic['annotations'],
|
|
154
|
+
'members': [{k: m[k] for k in ('name', 'type', 'annotations') if k in m}
|
|
155
|
+
for m in dic['members'][:3]]},
|
|
156
|
+
indent=2))
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
if __name__ == '__main__':
|
|
160
|
+
main()
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
"""Example: parse an IDL string and print interfaces, typedefs, unions and structs.
|
|
1
|
+
"""Example: parse an IDL string and print interfaces, typedefs, unions and structs.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Run (no install needed in a checkout)::
|
|
4
|
+
|
|
5
|
+
python examples/example.py
|
|
6
|
+
|
|
7
|
+
See also ``union_example.py`` and ``annotation_example.py`` in this folder.
|
|
8
|
+
"""
|
|
9
|
+
import _path # noqa: F401 (use the idl_parser of this checkout)
|
|
10
|
+
|
|
11
|
+
from idl_parser import parser
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main():
|
|
5
15
|
_parser = parser.IDLParser()
|
|
6
16
|
idl_str = '''
|
|
7
17
|
module my_module {
|
|
@@ -88,4 +98,4 @@ module my_module {
|
|
|
88
98
|
|
|
89
99
|
|
|
90
100
|
if __name__ == '__main__':
|
|
91
|
-
|
|
101
|
+
main()
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Example: read IDL unions (discriminated unions).
|
|
2
|
+
|
|
3
|
+
Shows how to get
|
|
4
|
+
- the discriminator type of a union (enum, integer, boolean),
|
|
5
|
+
- which case labels select each member (including several labels for one member),
|
|
6
|
+
- each member's type: primitive, struct, typedef of a sequence, or array,
|
|
7
|
+
- a union used as a struct member,
|
|
8
|
+
- the union as a plain dict (``to_dic``).
|
|
9
|
+
|
|
10
|
+
Run::
|
|
11
|
+
|
|
12
|
+
python examples/union_example.py
|
|
13
|
+
"""
|
|
14
|
+
import json
|
|
15
|
+
|
|
16
|
+
import _path # noqa: F401 (use the idl_parser of this checkout)
|
|
17
|
+
|
|
18
|
+
from idl_parser import parser
|
|
19
|
+
|
|
20
|
+
IDL = '''
|
|
21
|
+
module shapes {
|
|
22
|
+
enum ShapeKind { CIRCLE, RECTANGLE, POLYGON, POINT };
|
|
23
|
+
|
|
24
|
+
struct Point2D {
|
|
25
|
+
double x;
|
|
26
|
+
double y;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
typedef sequence<Point2D> PointSeq;
|
|
30
|
+
|
|
31
|
+
struct Rectangle {
|
|
32
|
+
Point2D top_left;
|
|
33
|
+
double width;
|
|
34
|
+
double height;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Discriminator is an enum. One member can be selected by several labels.
|
|
38
|
+
union Shape switch (ShapeKind) {
|
|
39
|
+
case CIRCLE:
|
|
40
|
+
double radius;
|
|
41
|
+
case RECTANGLE:
|
|
42
|
+
Rectangle rect;
|
|
43
|
+
case POLYGON:
|
|
44
|
+
PointSeq vertices;
|
|
45
|
+
case POINT:
|
|
46
|
+
Point2D point;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Discriminator is an integer type; labels are numbers (negative is fine).
|
|
50
|
+
union Value switch (long) {
|
|
51
|
+
case -1:
|
|
52
|
+
case 0:
|
|
53
|
+
string error_message;
|
|
54
|
+
case 1:
|
|
55
|
+
long long integer_value;
|
|
56
|
+
case 2:
|
|
57
|
+
double matrix[3][3];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Discriminator is boolean.
|
|
61
|
+
union OptionalDouble switch (boolean) {
|
|
62
|
+
case TRUE:
|
|
63
|
+
double value;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// A union can be a member of a struct.
|
|
67
|
+
struct ShapeMessage {
|
|
68
|
+
ShapeKind kind;
|
|
69
|
+
Shape shape;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
'''
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def describe_type(t):
|
|
76
|
+
"""Return a short human readable description of a member type."""
|
|
77
|
+
if t.is_primitive:
|
|
78
|
+
return 'primitive %s' % t.name
|
|
79
|
+
if t.is_array:
|
|
80
|
+
return 'array %s' % t.name
|
|
81
|
+
if t.is_typedef:
|
|
82
|
+
# Follow the typedef to see what it stands for.
|
|
83
|
+
inner = t.type
|
|
84
|
+
if inner.is_sequence:
|
|
85
|
+
return 'typedef %s = sequence of %s' % (t.full_path, inner.inner_type.name)
|
|
86
|
+
return 'typedef %s = %s' % (t.full_path, inner.name)
|
|
87
|
+
if t.is_struct:
|
|
88
|
+
return 'struct %s (members: %s)' % (t.full_path, ', '.join(m.name for m in t.members))
|
|
89
|
+
if t.is_union:
|
|
90
|
+
return 'union %s' % t.full_path
|
|
91
|
+
if t.is_enum:
|
|
92
|
+
return 'enum %s' % t.full_path
|
|
93
|
+
return t.name
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def print_union(union):
|
|
97
|
+
print('union %s switch (%s)' % (union.full_path, union.descriminator_kind))
|
|
98
|
+
for member in union.members:
|
|
99
|
+
labels = ', '.join(member.descriminator_value_associations)
|
|
100
|
+
print(' case %s -> %s: %s' % (labels, member.name, describe_type(member.type)))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def main():
|
|
104
|
+
global_module = parser.IDLParser().load(IDL)
|
|
105
|
+
shapes = global_module.module_by_name('shapes')
|
|
106
|
+
|
|
107
|
+
# All unions in a module
|
|
108
|
+
print('unions in module shapes:', [u.name for u in shapes.unions])
|
|
109
|
+
print()
|
|
110
|
+
|
|
111
|
+
for union in shapes.unions:
|
|
112
|
+
print_union(union)
|
|
113
|
+
print()
|
|
114
|
+
|
|
115
|
+
# Look up a member by name, and find the member for a discriminator value.
|
|
116
|
+
shape = shapes.union_by_name('Shape')
|
|
117
|
+
rect = shape.member_by_name('rect')
|
|
118
|
+
print('Shape.rect full path:', rect.full_path)
|
|
119
|
+
print('Shape.rect.top_left type:', rect.type.member_by_name('top_left').type.full_path)
|
|
120
|
+
|
|
121
|
+
value = shapes.union_by_name('Value')
|
|
122
|
+
for label in ('-1', '0', '1', '2'):
|
|
123
|
+
selected = [m.name for m in value.members if label in m.descriminator_value_associations]
|
|
124
|
+
print('Value with discriminator %s holds: %s' % (label, selected[0]))
|
|
125
|
+
print()
|
|
126
|
+
|
|
127
|
+
# The discriminator kind is a name; resolve it to its enum to list the labels.
|
|
128
|
+
kind = shapes.enum_by_name(shape.descriminator_kind)
|
|
129
|
+
print('%s values: %s' % (kind.name, [v.name for v in kind.values]))
|
|
130
|
+
print()
|
|
131
|
+
|
|
132
|
+
# A union used inside a struct
|
|
133
|
+
message = shapes.struct_by_name('ShapeMessage')
|
|
134
|
+
for m in message.members:
|
|
135
|
+
print('ShapeMessage.%s: %s' % (m.name, describe_type(m.type)))
|
|
136
|
+
print()
|
|
137
|
+
|
|
138
|
+
# The whole union as a dict (handy for JSON or code generators)
|
|
139
|
+
print(json.dumps(shapes.union_by_name('OptionalDouble').to_dic(), indent=2))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if __name__ == '__main__':
|
|
143
|
+
main()
|
|
@@ -195,16 +195,26 @@ class IDLModule(node.IDLNode):
|
|
|
195
195
|
values = []
|
|
196
196
|
while True:
|
|
197
197
|
ln, fn, t = token_buf.pop()
|
|
198
|
+
if t is None:
|
|
199
|
+
if self._verbose: sys.stdout.write('# Error. No ";" after const.\n')
|
|
200
|
+
raise InvalidIDLSyntaxError(message='No ";" after const definition')
|
|
198
201
|
if t == ';':
|
|
199
202
|
break
|
|
200
203
|
values.append(t)
|
|
201
204
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
# Split at '=': "<type> <name> = <value>" (issue #52).
|
|
206
|
+
# The value is every token after '=', so that an
|
|
207
|
+
# expression such as "1 + 2" is kept whole.
|
|
208
|
+
if '=' not in values:
|
|
209
|
+
raise InvalidIDLSyntaxError(ln, fn, 'No "=" in const definition "%s"' % ' '.join(values))
|
|
210
|
+
eq = values.index('=')
|
|
211
|
+
if eq < 2:
|
|
212
|
+
raise InvalidIDLSyntaxError(ln, fn, 'No type or name in const definition "%s"' % ' '.join(values))
|
|
213
|
+
if eq == len(values) - 1:
|
|
214
|
+
raise InvalidIDLSyntaxError(ln, fn, 'No value in const definition "%s"' % ' '.join(values))
|
|
215
|
+
value_ = ' '.join(values[eq+1:])
|
|
216
|
+
name_ = values[eq-1]
|
|
217
|
+
typename = ' '.join(values[:eq-1])
|
|
208
218
|
s = const.IDLConst(name_, typename, value_, self, filepath=filepath)
|
|
209
219
|
s._add_annotations(pending_annotations)
|
|
210
220
|
s_ = self.const_by_name(name_)
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import os, sys
|
|
2
2
|
import re
|
|
3
|
+
import functools
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from warnings import deprecated as _deprecated
|
|
8
|
+
except ImportError: # Python < 3.13 (PEP 702)
|
|
9
|
+
def _deprecated(message, *, category=DeprecationWarning, stacklevel=1):
|
|
10
|
+
"""Small stand-in for :func:`warnings.deprecated` (Python 3.13+):
|
|
11
|
+
warn with ``category`` each time the function is called."""
|
|
12
|
+
def decorator(func):
|
|
13
|
+
@functools.wraps(func)
|
|
14
|
+
def wrapper(*args, **kwargs):
|
|
15
|
+
warnings.warn(message, category=category, stacklevel=stacklevel + 1)
|
|
16
|
+
return func(*args, **kwargs)
|
|
17
|
+
wrapper.__deprecated__ = message
|
|
18
|
+
return wrapper
|
|
19
|
+
return decorator
|
|
3
20
|
|
|
4
21
|
from . import module, token_buffer, pragma as idl_pragma
|
|
22
|
+
from .token_buffer import _literal_end
|
|
5
23
|
from . import type as idl_type
|
|
6
24
|
from . import exception
|
|
7
25
|
|
|
@@ -24,6 +42,21 @@ class ConsoleTracker():
|
|
|
24
42
|
|
|
25
43
|
logger = ConsoleTracker()
|
|
26
44
|
|
|
45
|
+
|
|
46
|
+
def _format_code(code):
|
|
47
|
+
"""Put spaces around punctuation in code outside of literals and
|
|
48
|
+
comments, and squash runs of whitespace into one space."""
|
|
49
|
+
code = code.replace('{', ' { ')
|
|
50
|
+
# A single ':' (case label, inheritance), but not '::' (scoped name).
|
|
51
|
+
code = re.sub(r'(?<!:):(?!:)', ' : ', code)
|
|
52
|
+
code = code.replace(';', ' ;')
|
|
53
|
+
code = code.replace('(', ' ( ')
|
|
54
|
+
code = code.replace(',', ' , ')
|
|
55
|
+
code = code.replace('=', ' = ')
|
|
56
|
+
code = code.replace(')', ' ) ')
|
|
57
|
+
code = code.replace('}', ' } ')
|
|
58
|
+
return re.sub(r'\s+', ' ', code)
|
|
59
|
+
|
|
27
60
|
class IDLParser():
|
|
28
61
|
|
|
29
62
|
def __init__(self, idl_dirs=[], verbose=False):
|
|
@@ -58,7 +91,20 @@ class IDLParser():
|
|
|
58
91
|
def dirs(self):
|
|
59
92
|
return self._dirs
|
|
60
93
|
|
|
94
|
+
@_deprecated('IDLParser.prepare_input() is no longer used by load() and '
|
|
95
|
+
'does not look at string literals (issue #53). '
|
|
96
|
+
'load() and parse() now handle spaces inside "[ ]" and "< >" by themselves.')
|
|
61
97
|
def prepare_input(self, data):
|
|
98
|
+
"""Remove whitespace just inside ``[ ]`` and ``< >`` in ``data``.
|
|
99
|
+
|
|
100
|
+
No longer used by :meth:`load`: the same joining is now done on
|
|
101
|
+
tokens outside literals and comments, for :meth:`load` and
|
|
102
|
+
:meth:`parse` alike (see :func:`idl_parser.token_buffer.join_brackets`,
|
|
103
|
+
issue #53). Kept for compatibility; it does not look at literals.
|
|
104
|
+
|
|
105
|
+
.. deprecated:: 0.2.1
|
|
106
|
+
Calling it emits a :class:`DeprecationWarning`.
|
|
107
|
+
"""
|
|
62
108
|
from re import compile, UNICODE, MULTILINE
|
|
63
109
|
flags = UNICODE | MULTILINE
|
|
64
110
|
|
|
@@ -80,7 +126,6 @@ class IDLParser():
|
|
|
80
126
|
|
|
81
127
|
def load(self, input_str, include_dirs=[], filepath=None):
|
|
82
128
|
self._dirs = self._dirs + include_dirs
|
|
83
|
-
input_str = self.prepare_input(input_str)
|
|
84
129
|
lines = [(i+1, filepath, l) for i, l in enumerate(input_str.split('\n'))]
|
|
85
130
|
self.parse_lines(lines, filepath=filepath)
|
|
86
131
|
return self._global_module
|
|
@@ -308,46 +353,51 @@ class IDLParser():
|
|
|
308
353
|
|
|
309
354
|
|
|
310
355
|
def _clear_comments(self, lines):
|
|
356
|
+
"""Remove comments and put spaces around punctuation so that each
|
|
357
|
+
token is separated by whitespace.
|
|
358
|
+
|
|
359
|
+
Lines are scanned one character at a time. String literals
|
|
360
|
+
(``"..."``) and character literals (``'...'``) are copied as they
|
|
361
|
+
are: ``//`` or ``/*`` inside them is not a comment (issue #50), and
|
|
362
|
+
no spaces are put into them. A ``:`` right after a literal, as in
|
|
363
|
+
``case 'a':``, is separated like any other ``:`` (issue #49).
|
|
364
|
+
"""
|
|
311
365
|
output_lines = []
|
|
312
366
|
in_comment = False
|
|
313
367
|
|
|
314
368
|
for line_number, file_name, line in lines:
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
369
|
+
parts = [] # code (formatted) and literals (as they are), in order
|
|
370
|
+
code = ''
|
|
371
|
+
i = 0
|
|
372
|
+
n = len(line)
|
|
373
|
+
while i < n:
|
|
374
|
+
if in_comment:
|
|
375
|
+
end = line.find('*/', i)
|
|
376
|
+
if end < 0:
|
|
377
|
+
break # the comment continues on the next line
|
|
323
378
|
in_comment = False
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
elif
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
elif token.startswith('//'):
|
|
330
|
-
break # ignore this line
|
|
331
|
-
|
|
332
|
-
elif token.find('/*') >= 0:
|
|
379
|
+
i = end + 2
|
|
380
|
+
code = code + ' '
|
|
381
|
+
elif line.startswith('//', i):
|
|
382
|
+
break # ignore the rest of this line
|
|
383
|
+
elif line.startswith('/*', i):
|
|
333
384
|
in_comment = True
|
|
334
|
-
|
|
385
|
+
i = i + 2
|
|
386
|
+
code = code + ' '
|
|
387
|
+
elif line[i] in '"\'':
|
|
388
|
+
end = _literal_end(line, i)
|
|
389
|
+
parts.append(_format_code(code))
|
|
390
|
+
parts.append(line[i:end].rstrip('\r\n'))
|
|
391
|
+
code = ''
|
|
392
|
+
i = end
|
|
335
393
|
else:
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
token = token.replace('(', ' ( ')
|
|
344
|
-
token = token.replace(',', ' , ')
|
|
345
|
-
token = token.replace('=', ' = ')
|
|
346
|
-
token = token.replace(')', ' ) ')
|
|
347
|
-
token = token.replace('}', ' } ')
|
|
348
|
-
output_line = output_line + ' ' + token.strip()
|
|
349
|
-
if len(output_line.strip()) > 0:
|
|
350
|
-
output_lines.append((line_number, file_name, output_line.strip() + '\n'))
|
|
394
|
+
code = code + line[i]
|
|
395
|
+
i = i + 1
|
|
396
|
+
parts.append(_format_code(code))
|
|
397
|
+
|
|
398
|
+
output_line = ''.join(parts).strip()
|
|
399
|
+
if len(output_line) > 0:
|
|
400
|
+
output_lines.append((line_number, file_name, output_line + '\n'))
|
|
351
401
|
|
|
352
402
|
return output_lines
|
|
353
403
|
|