docs: cleanup: Rephrase and correct typos
[barectf.git] / barectf / config_parse_v3.py
CommitLineData
4810b707
PP
1# The MIT License (MIT)
2#
3# Copyright (c) 2015-2020 Philippe Proulx <pproulx@efficios.com>
4#
5# Permission is hereby granted, free of charge, to any person obtaining
6# a copy of this software and associated documentation files (the
7# "Software"), to deal in the Software without restriction, including
8# without limitation the rights to use, copy, modify, merge, publish,
9# distribute, sublicense, and/or sell copies of the Software, and to
10# permit persons to whom the Software is furnished to do so, subject to
11# the following conditions:
12#
13# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24import barectf.config_parse_common as barectf_config_parse_common
25from barectf.config_parse_common import _ConfigurationParseError
26from barectf.config_parse_common import _append_error_ctx
2d55dc7d 27from barectf.config_parse_common import _MapNode
4810b707 28import barectf.config as barectf_config
be9f12dc 29from barectf.config import _OptStructFt
4810b707
PP
30import collections
31import uuid
2d55dc7d
PP
32from barectf.typing import Count, Alignment, VersionNumber
33from typing import Optional, List, Dict, Any, TextIO, Set, Iterable, Callable, Tuple, Type
34import typing
4810b707
PP
35
36
37# A barectf 3 YAML configuration parser.
38#
39# When you build such a parser, it parses the configuration node `node`
40# (already loaded from the file having the path `path`) and creates a
41# corresponding `barectf.Configuration` object which you can get with
42# the `config` property.
43#
44# See the comments of _parse() for more implementation details about the
45# parsing stages and general strategy.
46class _Parser(barectf_config_parse_common._Parser):
47 # Builds a barectf 3 YAML configuration parser and parses the root
2d55dc7d
PP
48 # configuration node `node` (already loaded from the file-like
49 # object `root_file`).
50 def __init__(self, root_file: TextIO, node: barectf_config_parse_common._ConfigNodeV3,
51 with_pkg_include_dir: bool, inclusion_dirs: Optional[List[str]],
52 ignore_include_not_found: bool):
53 super().__init__(root_file, node, with_pkg_include_dir, inclusion_dirs,
54 ignore_include_not_found, VersionNumber(3))
be9f12dc
PP
55 self._ft_cls_name_to_create_method: Dict[str, Callable[[_MapNode],
56 List[barectf_config._FieldType]]] = {
4810b707
PP
57 'unsigned-integer': self._create_int_ft,
58 'signed-integer': self._create_int_ft,
59 'unsigned-enumeration': self._create_enum_ft,
60 'signed-enumeration': self._create_enum_ft,
61 'real': self._create_real_ft,
62 'string': self._create_string_ft,
63 'static-array': self._create_static_array_ft,
be9f12dc 64 'dynamic-array': self._create_dynamic_array_ft,
4810b707
PP
65 'structure': self._create_struct_ft,
66 }
67 self._parse()
68
69 # Validates the alignment `alignment`, raising a
70 # `_ConfigurationParseError` exception using `ctx_obj_name` if it's
71 # invalid.
72 @staticmethod
2d55dc7d 73 def _validate_alignment(alignment: Alignment, ctx_obj_name: str):
4810b707
PP
74 assert alignment >= 1
75
76 # check for power of two
77 if (alignment & (alignment - 1)) != 0:
78 raise _ConfigurationParseError(ctx_obj_name,
79 f'Invalid alignment (not a power of two): {alignment}')
80
81 # Validates the TSDL identifier `iden`, raising a
82 # `_ConfigurationParseError` exception using `ctx_obj_name` and
83 # `prop` to format the message if it's invalid.
84 @staticmethod
2d55dc7d 85 def _validate_iden(iden: str, ctx_obj_name: str, prop: str):
4810b707
PP
86 assert type(iden) is str
87 ctf_keywords = {
88 'align',
89 'callsite',
90 'clock',
91 'enum',
92 'env',
93 'event',
94 'floating_point',
95 'integer',
96 'stream',
97 'string',
98 'struct',
99 'trace',
100 'typealias',
101 'typedef',
102 'variant',
103 }
104
105 if iden in ctf_keywords:
106 msg = f'Invalid {prop} (not a valid identifier): `{iden}`'
107 raise _ConfigurationParseError(ctx_obj_name, msg)
108
109 @staticmethod
2d55dc7d 110 def _alignment_prop(ft_node: _MapNode, prop_name: str) -> Alignment:
4810b707
PP
111 alignment = ft_node.get(prop_name)
112
113 if alignment is not None:
114 _Parser._validate_alignment(alignment, '`prop_name` property')
115
2d55dc7d 116 return Alignment(alignment)
4810b707
PP
117
118 @property
2d55dc7d
PP
119 def _trace_type_node(self) -> _MapNode:
120 return self.config_node['trace']['type']
4810b707
PP
121
122 @staticmethod
2d55dc7d 123 def _byte_order_from_node(node: str) -> barectf_config.ByteOrder:
4810b707
PP
124 return {
125 'big-endian': barectf_config.ByteOrder.BIG_ENDIAN,
126 'little-endian': barectf_config.ByteOrder.LITTLE_ENDIAN,
127 }[node]
128
129 # Creates a bit array field type having the type `ft_type` from the
130 # bit array field type node `ft_node`, passing the additional
131 # `*args` to ft_type.__init__().
2d55dc7d
PP
132 def _create_common_bit_array_ft(self, ft_node: _MapNode,
133 ft_type: Type[barectf_config._BitArrayFieldType],
134 default_alignment: Optional[Alignment],
135 *args) -> barectf_config._BitArrayFieldType:
4810b707
PP
136 alignment = self._alignment_prop(ft_node, 'alignment')
137
138 if alignment is None:
139 alignment = default_alignment
140
4c91e769 141 return ft_type(ft_node['size'], alignment, *args)
4810b707
PP
142
143 # Creates an integer field type having the type `ft_type` from the
144 # integer field type node `ft_node`, passing the additional `*args`
145 # to ft_type.__init__().
2d55dc7d
PP
146 def _create_common_int_ft(self, ft_node: _MapNode,
147 ft_type: Type[barectf_config._IntegerFieldType], *args) -> barectf_config._IntegerFieldType:
4810b707
PP
148 preferred_display_base = {
149 'binary': barectf_config.DisplayBase.BINARY,
150 'octal': barectf_config.DisplayBase.OCTAL,
151 'decimal': barectf_config.DisplayBase.DECIMAL,
152 'hexadecimal': barectf_config.DisplayBase.HEXADECIMAL,
153 }[ft_node.get('preferred-display-base', 'decimal')]
2d55dc7d
PP
154 return typing.cast(barectf_config._IntegerFieldType,
155 self._create_common_bit_array_ft(ft_node, ft_type, None,
156 preferred_display_base, *args))
4810b707
PP
157
158 # Creates an integer field type from the unsigned/signed integer
159 # field type node `ft_node`.
be9f12dc 160 def _create_int_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
4810b707
PP
161 ft_type = {
162 'unsigned-integer': barectf_config.UnsignedIntegerFieldType,
163 'signed-integer': barectf_config.SignedIntegerFieldType,
164 }[ft_node['class']]
be9f12dc 165 return [self._create_common_int_ft(ft_node, ft_type)]
4810b707
PP
166
167 # Creates an enumeration field type from the unsigned/signed
168 # enumeration field type node `ft_node`.
be9f12dc 169 def _create_enum_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
4810b707
PP
170 ft_type = {
171 'unsigned-enumeration': barectf_config.UnsignedEnumerationFieldType,
172 'signed-enumeration': barectf_config.SignedEnumerationFieldType,
173 }[ft_node['class']]
174 mappings = collections.OrderedDict()
175
98bc60de 176 for label, mapping_node in ft_node['mappings'].items():
4810b707
PP
177 ranges = set()
178
179 for range_node in mapping_node:
180 if type(range_node) is list:
181 ranges.add(barectf_config.EnumerationFieldTypeMappingRange(range_node[0],
182 range_node[1]))
183 else:
184 assert type(range_node) is int
185 ranges.add(barectf_config.EnumerationFieldTypeMappingRange(range_node,
186 range_node))
187
188 mappings[label] = barectf_config.EnumerationFieldTypeMapping(ranges)
189
be9f12dc
PP
190 return [typing.cast(barectf_config._EnumerationFieldType,
191 self._create_common_int_ft(ft_node, ft_type,
192 barectf_config.EnumerationFieldTypeMappings(mappings)))]
4810b707
PP
193
194 # Creates a real field type from the real field type node `ft_node`.
be9f12dc
PP
195 def _create_real_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
196 return [typing.cast(barectf_config.RealFieldType,
197 self._create_common_bit_array_ft(ft_node, barectf_config.RealFieldType,
198 Alignment(8)))]
4810b707
PP
199
200 # Creates a string field type from the string field type node
201 # `ft_node`.
be9f12dc
PP
202 def _create_string_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
203 return [barectf_config.StringFieldType()]
4810b707 204
be9f12dc 205 def _create_array_ft(self, ft_type, ft_node: _MapNode, **kwargs) -> barectf_config._ArrayFieldType:
4810b707
PP
206 prop_name = 'element-field-type'
207
208 try:
be9f12dc 209 element_fts = self._create_fts(ft_node[prop_name])
4810b707
PP
210 except _ConfigurationParseError as exc:
211 _append_error_ctx(exc, f'`{prop_name}` property')
212
be9f12dc
PP
213 if len(element_fts) != 1 or isinstance(element_fts[0], (barectf_config.StructureFieldType,
214 barectf_config.DynamicArrayFieldType)):
215 raise _ConfigurationParseError(f'`{prop_name}` property',
216 'Nested structure and dynamic array field types are not supported')
217
218 return ft_type(element_field_type=element_fts[0], **kwargs)
219
220 # Creates a static array field type from the static array field type
221 # node `ft_node`.
222 def _create_static_array_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
223 return [typing.cast(barectf_config.StaticArrayFieldType,
224 self._create_array_ft(barectf_config.StaticArrayFieldType, ft_node,
225 length=ft_node['length']))]
226
227 # Creates a dynamic array field type from the dynamic array field
228 # type node `ft_node`.
229 def _create_dynamic_array_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
230 # create length unsigned integer field type
231 len_ft = barectf_config.UnsignedIntegerFieldType(32, alignment=Alignment(8))
232 return [
233 len_ft,
234 typing.cast(barectf_config.DynamicArrayFieldType,
235 self._create_array_ft(barectf_config.DynamicArrayFieldType, ft_node,
236 length_field_type=len_ft))
237 ]
4810b707
PP
238
239 # Creates structure field type members from the structure field type
240 # members node `members_node`.
241 #
242 # `prop_name` is the name of the property of which `members_node` is
243 # the value.
2d55dc7d 244 def _create_struct_ft_members(self, members_node: List[_MapNode], prop_name: str):
4810b707 245 members = collections.OrderedDict()
2d55dc7d 246 member_names: Set[str] = set()
4810b707
PP
247
248 for member_node in members_node:
249 member_name, member_node = list(member_node.items())[0]
250
251 if member_name in member_names:
252 raise _ConfigurationParseError(f'`{prop_name}` property',
253 f'Duplicate member `{member_name}`')
254
255 self._validate_iden(member_name, f'`{prop_name}` property',
256 'structure field type member name')
257 member_names.add(member_name)
258 ft_prop_name = 'field-type'
259 ft_node = member_node[ft_prop_name]
260
261 try:
6bc97055 262 if ft_node['class'] in ['structure']:
4810b707 263 raise _ConfigurationParseError(f'`{ft_prop_name}` property',
6bc97055 264 'Nested structure field types are not supported')
4810b707
PP
265
266 try:
be9f12dc 267 member_fts = self._create_fts(ft_node)
4810b707 268 except _ConfigurationParseError as exc:
be9f12dc 269 _append_error_ctx(exc, f'`{ft_prop_name}` property')
4810b707
PP
270 except _ConfigurationParseError as exc:
271 _append_error_ctx(exc, f'Structure field type member `{member_name}`')
272
be9f12dc
PP
273 if len(member_fts) == 2:
274 # The only case where this happens is a dynamic array
275 # field type node which generates an unsigned integer
276 # field type for the length and the dynamic array field
277 # type itself.
278 assert type(member_fts[1]) is barectf_config.DynamicArrayFieldType
279 members[f'__{member_name}_len'] = barectf_config.StructureFieldTypeMember(member_fts[0])
280 else:
281 assert len(member_fts) == 1
282
283 members[member_name] = barectf_config.StructureFieldTypeMember(member_fts[-1])
4810b707
PP
284
285 return barectf_config.StructureFieldTypeMembers(members)
286
287 # Creates a structure field type from the structure field type node
288 # `ft_node`.
be9f12dc 289 def _create_struct_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
4810b707
PP
290 minimum_alignment = self._alignment_prop(ft_node, 'minimum-alignment')
291
292 if minimum_alignment is None:
293 minimum_alignment = 1
294
295 members = None
296 prop_name = 'members'
297 members_node = ft_node.get(prop_name)
298
299 if members_node is not None:
300 members = self._create_struct_ft_members(members_node, prop_name)
301
be9f12dc 302 return [barectf_config.StructureFieldType(minimum_alignment, members)]
4810b707 303
be9f12dc
PP
304 # Creates field types from the field type node `ft_node`.
305 def _create_fts(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
4810b707
PP
306 return self._ft_cls_name_to_create_method[ft_node['class']](ft_node)
307
be9f12dc 308 # Creates field types from the field type node `parent_node[key]`
4810b707 309 # if it exists.
be9f12dc 310 def _try_create_fts(self, parent_node: _MapNode, key: str) -> Optional[List[barectf_config._FieldType]]:
4810b707 311 if key not in parent_node:
2d55dc7d 312 return None
4810b707
PP
313
314 try:
be9f12dc 315 return self._create_fts(parent_node[key])
4810b707
PP
316 except _ConfigurationParseError as exc:
317 _append_error_ctx(exc, f'`{key}` property')
318
2d55dc7d
PP
319 # satisfy static type checker (never reached)
320 raise
321
be9f12dc
PP
322 # Like _try_create_fts(), but casts the result's type (first and
323 # only element) to `barectf_config.StructureFieldType` to satisfy
324 # static type checkers.
2d55dc7d 325 def _try_create_struct_ft(self, parent_node: _MapNode, key: str) -> _OptStructFt:
be9f12dc
PP
326 fts = self._try_create_fts(parent_node, key)
327
328 if fts is None:
329 return None
330
331 return typing.cast(barectf_config.StructureFieldType, fts[0])
2d55dc7d 332
4810b707
PP
333 # Returns the total number of members in the structure field type
334 # node `ft_node` if it exists, otherwise 0.
335 @staticmethod
2d55dc7d 336 def _total_struct_ft_node_members(ft_node: Optional[_MapNode]) -> Count:
4810b707 337 if ft_node is None:
2d55dc7d 338 return Count(0)
4810b707
PP
339
340 members_node = ft_node.get('members')
341
342 if members_node is None:
2d55dc7d 343 return Count(0)
4810b707 344
2d55dc7d 345 return Count(len(members_node))
4810b707 346
e8f0d548
PP
347 # Creates an event record type from the event record type node
348 # `ert_node` named `name`.
4810b707 349 #
e8f0d548
PP
350 # `ert_member_count` is the total number of structure field type
351 # members within the event record type so far (from the common part
352 # in its data stream type). For example, if the data stream type has
353 # an event record header field type with `id` and `timestamp`
354 # members, then `ert_member_count` is 2.
355 def _create_ert(self, name: str, ert_node: _MapNode,
356 ert_member_count: Count) -> barectf_config.EventRecordType:
4810b707 357 try:
e8f0d548 358 self._validate_iden(name, '`name` property', 'event record type name')
4810b707 359
e8f0d548 360 # make sure the event record type is not empty
4810b707
PP
361 spec_ctx_ft_prop_name = 'specific-context-field-type'
362 payload_ft_prop_name = 'payload-field-type'
e8f0d548
PP
363 ert_member_count = Count(ert_member_count + self._total_struct_ft_node_members(ert_node.get(spec_ctx_ft_prop_name)))
364 ert_member_count = Count(ert_member_count + self._total_struct_ft_node_members(ert_node.get(payload_ft_prop_name)))
4810b707 365
e8f0d548
PP
366 if ert_member_count == 0:
367 raise _ConfigurationParseError('Event record type',
368 'Event record type is empty (no members).')
4810b707 369
e8f0d548
PP
370 # create event record type
371 return barectf_config.EventRecordType(name, ert_node.get('log-level'),
372 self._try_create_struct_ft(ert_node,
2d55dc7d 373 spec_ctx_ft_prop_name),
e8f0d548 374 self._try_create_struct_ft(ert_node,
2d55dc7d 375 payload_ft_prop_name))
4810b707 376 except _ConfigurationParseError as exc:
e8f0d548 377 _append_error_ctx(exc, f'Event record type `{name}`')
4810b707 378
2d55dc7d
PP
379 # satisfy static type checker (never reached)
380 raise
381
4810b707
PP
382 # Returns the effective feature field type for the field type
383 # node `parent_node[key]`, if any.
384 #
385 # Returns:
386 #
387 # If `parent_node[key]` is `False`:
388 # `None`.
389 #
390 # If `parent_node[key]` is `True`:
391 # `barectf_config.DEFAULT_FIELD_TYPE`.
392 #
393 # If `parent_node[key]` doesn't exist:
394 # `none` (parameter).
395 #
396 # Otherwise:
397 # A created field type.
2d55dc7d 398 def _feature_ft(self, parent_node: _MapNode, key: str, none: Any = None) -> Any:
4810b707
PP
399 if key not in parent_node:
400 # missing: default feature field type
401 return none
402
403 ft_node = parent_node[key]
404 assert ft_node is not None
405
406 if ft_node is True:
407 # default feature field type
408 return barectf_config.DEFAULT_FIELD_TYPE
409
410 if ft_node is False:
411 # disabled feature
412 return None
413
414 assert type(ft_node) is collections.OrderedDict
be9f12dc 415 return self._create_fts(ft_node)[0]
4810b707 416
e8f0d548 417 def _create_dst(self, name: str, dst_node: _MapNode) -> barectf_config.DataStreamType:
4810b707 418 try:
e8f0d548
PP
419 # validate data stream type's name
420 self._validate_iden(name, '`name` property', 'data stream type name')
4810b707
PP
421
422 # get default clock type, if any
423 def_clk_type = None
424 prop_name = '$default-clock-type-name'
e8f0d548 425 def_clk_type_name = dst_node.get(prop_name)
4810b707
PP
426
427 if def_clk_type_name is not None:
428 try:
429 def_clk_type = self._clk_type(def_clk_type_name, prop_name)
430 except _ConfigurationParseError as exc:
431 _append_error_ctx(exc, f'`{prop_name}` property')
432
433 # create feature field types
434 pkt_total_size_ft = barectf_config.DEFAULT_FIELD_TYPE
435 pkt_content_size_ft = barectf_config.DEFAULT_FIELD_TYPE
462e49b3
PP
436 pkt_beginning_ts_ft = None
437 pkt_end_ts_ft = None
470ff4d9 438 pkt_disc_er_counter_snap_ft = barectf_config.DEFAULT_FIELD_TYPE
e8f0d548 439 ert_id_ft = barectf_config.DEFAULT_FIELD_TYPE
462e49b3 440 ert_ts_ft = None
af09c4fc 441 pkt_seq_num_ft = None
4810b707
PP
442
443 if def_clk_type is not None:
e8f0d548 444 # The data stream type has a default clock type.
462e49b3
PP
445 # Initialize the packet beginning timestamp, packet end
446 # timestamp, and event record timestamp field types to
447 # default field types.
4810b707 448 #
e8f0d548
PP
449 # This means your data stream type node only needs a
450 # default clock type name to enable those features
4810b707 451 # automatically. Those features do not add any parameter
e8f0d548 452 # to the event tracing functions.
462e49b3
PP
453 pkt_beginning_ts_ft = barectf_config.DEFAULT_FIELD_TYPE
454 pkt_end_ts_ft = barectf_config.DEFAULT_FIELD_TYPE
455 ert_ts_ft = barectf_config.DEFAULT_FIELD_TYPE
4810b707 456
e8f0d548 457 features_node = dst_node.get('$features')
4810b707
PP
458
459 if features_node is not None:
460 # create packet feature field types
461 pkt_node = features_node.get('packet')
462
463 if pkt_node is not None:
464 pkt_total_size_ft = self._feature_ft(pkt_node, 'total-size-field-type',
465 pkt_total_size_ft)
466 pkt_content_size_ft = self._feature_ft(pkt_node, 'content-size-field-type',
467 pkt_content_size_ft)
462e49b3
PP
468 pkt_beginning_ts_ft = self._feature_ft(pkt_node,
469 'beginning-timestamp-field-type',
470 pkt_beginning_ts_ft)
471 pkt_end_ts_ft = self._feature_ft(pkt_node, 'end-timestamp-field-type',
472 pkt_end_ts_ft)
470ff4d9 473 pkt_disc_er_counter_snap_ft = self._feature_ft(pkt_node,
e8f0d548 474 'discarded-event-records-counter-snapshot-field-type',
470ff4d9 475 pkt_disc_er_counter_snap_ft)
af09c4fc
JL
476 pkt_seq_num_ft = self._feature_ft(pkt_node, 'sequence-number-field-type',
477 pkt_seq_num_ft)
4810b707 478
e8f0d548
PP
479 # create event record feature field types
480 er_node = features_node.get('event-record')
4810b707
PP
481 type_id_ft_prop_name = 'type-id-field-type'
482
e8f0d548
PP
483 if er_node is not None:
484 ert_id_ft = self._feature_ft(er_node, type_id_ft_prop_name, ert_id_ft)
462e49b3 485 ert_ts_ft = self._feature_ft(er_node, 'timestamp-field-type', ert_ts_ft)
4810b707 486
e8f0d548
PP
487 erts_prop_name = 'event-record-types'
488 ert_count = len(dst_node[erts_prop_name])
4810b707
PP
489
490 try:
e8f0d548 491 if ert_id_ft is None and ert_count > 1:
4810b707 492 raise _ConfigurationParseError(f'`{type_id_ft_prop_name}` property',
e8f0d548 493 'Event record type ID field type feature is required because data stream type has more than one event record type')
4810b707 494
e8f0d548
PP
495 if isinstance(ert_id_ft, barectf_config._IntegerFieldType):
496 ert_id_int_ft = typing.cast(barectf_config._IntegerFieldType, ert_id_ft)
2d55dc7d 497
e8f0d548 498 if ert_count > (1 << ert_id_int_ft.size):
2d55dc7d 499 raise _ConfigurationParseError(f'`{type_id_ft_prop_name}` property',
e8f0d548 500 f'Field type\'s size ({ert_id_int_ft.size} bits) is too small to accomodate {ert_count} event record types')
4810b707 501 except _ConfigurationParseError as exc:
e8f0d548 502 exc._append_ctx('`event-record` property')
4810b707
PP
503 _append_error_ctx(exc, '`$features` property')
504
e8f0d548
PP
505 pkt_features = barectf_config.DataStreamTypePacketFeatures(pkt_total_size_ft,
506 pkt_content_size_ft,
462e49b3
PP
507 pkt_beginning_ts_ft,
508 pkt_end_ts_ft,
af09c4fc
JL
509 pkt_disc_er_counter_snap_ft,
510 pkt_seq_num_ft)
462e49b3 511 er_features = barectf_config.DataStreamTypeEventRecordFeatures(ert_id_ft, ert_ts_ft)
e8f0d548 512 features = barectf_config.DataStreamTypeFeatures(pkt_features, er_features)
4810b707
PP
513
514 # create packet context (structure) field type extra members
515 pkt_ctx_ft_extra_members = None
516 prop_name = 'packet-context-field-type-extra-members'
e8f0d548 517 pkt_ctx_ft_extra_members_node = dst_node.get(prop_name)
4810b707
PP
518
519 if pkt_ctx_ft_extra_members_node is not None:
520 pkt_ctx_ft_extra_members = self._create_struct_ft_members(pkt_ctx_ft_extra_members_node,
521 prop_name)
522
523 # check for illegal packet context field type member names
524 reserved_member_names = {
525 'packet_size',
526 'content_size',
527 'timestamp_begin',
528 'timestamp_end',
529 'events_discarded',
530 'packet_seq_num',
531 }
532
533 for member_name in pkt_ctx_ft_extra_members:
534 if member_name in reserved_member_names:
535 raise _ConfigurationParseError(f'`{prop_name}` property',
536 f'Packet context field type member name `{member_name}` is reserved.')
537
e8f0d548
PP
538 # create event record types
539 er_header_common_ctx_member_count = Count(0)
4810b707 540
e8f0d548
PP
541 if er_features.type_id_field_type is not None:
542 er_header_common_ctx_member_count = Count(er_header_common_ctx_member_count + 1)
4810b707 543
462e49b3 544 if er_features.timestamp_field_type is not None:
e8f0d548 545 er_header_common_ctx_member_count = Count(er_header_common_ctx_member_count + 1)
4810b707 546
e8f0d548
PP
547 er_common_ctx_ft_prop_name = 'event-record-common-context-field-type'
548 er_common_ctx_ft_node = dst_node.get(er_common_ctx_ft_prop_name)
549 er_header_common_ctx_member_count = Count(er_header_common_ctx_member_count + self._total_struct_ft_node_members(er_common_ctx_ft_node))
550 erts = set()
4810b707 551
e8f0d548
PP
552 for ert_name, ert_node in dst_node[erts_prop_name].items():
553 erts.add(self._create_ert(ert_name, ert_node, er_header_common_ctx_member_count))
4810b707 554
e8f0d548
PP
555 # create data stream type
556 return barectf_config.DataStreamType(name, erts, def_clk_type, features,
557 pkt_ctx_ft_extra_members,
558 self._try_create_struct_ft(dst_node,
559 er_common_ctx_ft_prop_name))
4810b707 560 except _ConfigurationParseError as exc:
e8f0d548 561 _append_error_ctx(exc, f'Data data stream type `{name}`')
4810b707 562
2d55dc7d
PP
563 # satisfy static type checker (never reached)
564 raise
565
566 def _clk_type(self, name: str, prop_name: str) -> barectf_config.ClockType:
4810b707
PP
567 clk_type = self._clk_types.get(name)
568
569 if clk_type is None:
570 raise _ConfigurationParseError(f'`{prop_name}` property',
571 f'Clock type `{name}` does not exist')
572
573 return clk_type
574
2d55dc7d 575 def _create_clk_type(self, name: str, clk_type_node: _MapNode) -> barectf_config.ClockType:
4810b707
PP
576 self._validate_iden(name, '`name` property', 'clock type name')
577 clk_type_uuid = None
578 uuid_node = clk_type_node.get('uuid')
579
580 if uuid_node is not None:
581 clk_type_uuid = uuid.UUID(uuid_node)
582
583 offset_seconds = 0
2d55dc7d 584 offset_cycles = Count(0)
4810b707
PP
585 offset_node = clk_type_node.get('offset')
586
587 if offset_node is not None:
588 offset_seconds = offset_node.get('seconds', 0)
2d55dc7d 589 offset_cycles = offset_node.get('cycles', Count(0))
4810b707
PP
590
591 return barectf_config.ClockType(name, clk_type_node.get('frequency', int(1e9)),
592 clk_type_uuid, clk_type_node.get('description'),
593 clk_type_node.get('precision', 0),
594 barectf_config.ClockTypeOffset(offset_seconds, offset_cycles),
21910b4f 595 clk_type_node.get('origin-is-unix-epoch', True))
4810b707
PP
596
597 def _create_clk_types(self):
598 self._clk_types = {}
599
600 for clk_type_name, clk_type_node in self._trace_type_node.get('clock-types', {}).items():
601 self._clk_types[clk_type_name] = self._create_clk_type(clk_type_name, clk_type_node)
602
603 def _create_trace_type(self):
604 try:
e8f0d548 605 # create clock types (_create_dst() needs them)
4810b707
PP
606 self._create_clk_types()
607
608 # get UUID
609 trace_type_uuid = None
610 uuid_node = self._trace_type_node.get('uuid')
611
612 if uuid_node is not None:
613 if uuid_node == 'auto':
614 trace_type_uuid = uuid.uuid1()
615 else:
616 trace_type_uuid = uuid.UUID(uuid_node)
617
618 # create feature field types
619 magic_ft = barectf_config.DEFAULT_FIELD_TYPE
620 uuid_ft = None
e8f0d548 621 dst_id_ft = barectf_config.DEFAULT_FIELD_TYPE
4810b707
PP
622
623 if trace_type_uuid is not None:
624 # Trace type has a UUID: initialize UUID field type to
625 # a default field type.
626 uuid_ft = barectf_config.DEFAULT_FIELD_TYPE
627
628 features_node = self._trace_type_node.get('$features')
e8f0d548 629 dst_id_ft_prop_name = 'data-stream-type-id-field-type'
4810b707
PP
630
631 if features_node is not None:
632 magic_ft = self._feature_ft(features_node, 'magic-field-type',
633 magic_ft)
634 uuid_ft = self._feature_ft(features_node, 'uuid-field-type', uuid_ft)
e8f0d548 635 dst_id_ft = self._feature_ft(features_node, dst_id_ft_prop_name, dst_id_ft)
4810b707 636
e8f0d548
PP
637 dsts_prop_name = 'data-stream-types'
638 dst_count = len(self._trace_type_node[dsts_prop_name])
4810b707
PP
639
640 try:
e8f0d548
PP
641 if dst_id_ft is None and dst_count > 1:
642 raise _ConfigurationParseError(f'`{dst_id_ft_prop_name}` property',
643 'Data stream type ID field type feature is required because trace type has more than one data stream type')
4810b707 644
e8f0d548
PP
645 if isinstance(dst_id_ft, barectf_config._FieldType) and dst_count > (1 << dst_id_ft.size):
646 raise _ConfigurationParseError(f'`{dst_id_ft_prop_name}` property',
647 f'Field type\'s size ({dst_id_ft.size} bits) is too small to accomodate {dst_count} data stream types')
4810b707
PP
648 except _ConfigurationParseError as exc:
649 _append_error_ctx(exc, '`$features` property')
650
e8f0d548 651 features = barectf_config.TraceTypeFeatures(magic_ft, uuid_ft, dst_id_ft)
4810b707 652
e8f0d548
PP
653 # create data stream types
654 dsts = set()
4810b707 655
e8f0d548
PP
656 for dst_name, dst_node in self._trace_type_node[dsts_prop_name].items():
657 dsts.add(self._create_dst(dst_name, dst_node))
4810b707
PP
658
659 # create trace type
a209cf4d
EB
660 if self._trace_byte_order_prop_key == 'native-byte-order':
661 trace_type_cls = barectf_config.TraceType
662 else:
663 trace_type_cls = barectf_config.TraceTypeWithUnknownNativeByteOrder
664
665 return trace_type_cls(self._trace_byte_order, dsts, trace_type_uuid, features)
4810b707
PP
666 except _ConfigurationParseError as exc:
667 _append_error_ctx(exc, 'Trace type')
668
669 def _create_trace(self):
670 try:
671 trace_type = self._create_trace_type()
2d55dc7d 672 trace_node = self.config_node['trace']
4810b707
PP
673 env = None
674 env_node = trace_node.get('environment')
675
676 if env_node is not None:
677 # validate each environment variable name
678 for name in env_node:
679 self._validate_iden(name, '`environment` property',
680 'environment variable name')
681
682 # the node already has the expected structure
683 env = barectf_config.TraceEnvironment(env_node)
684
685 return barectf_config.Trace(trace_type, env)
686
687 except _ConfigurationParseError as exc:
688 _append_error_ctx(exc, 'Trace')
689
690 def _create_config(self):
691 # create trace first
692 trace = self._create_trace()
693
e8f0d548
PP
694 # find default data stream type, if any
695 def_dst = None
4810b707 696
e8f0d548 697 for dst_name, dst_node in self._trace_type_node['data-stream-types'].items():
4810b707 698 prop_name = '$is-default'
e8f0d548 699 is_default = dst_node.get(prop_name)
4810b707
PP
700
701 if is_default is True:
e8f0d548 702 if def_dst is not None:
4810b707 703 exc = _ConfigurationParseError(f'`{prop_name}` property',
e8f0d548
PP
704 f'Duplicate default data stream type (`{def_dst.name}`)')
705 exc._append_ctx(f'Data stream type `{dst_name}`')
4810b707
PP
706 _append_error_ctx(exc, 'Trace type')
707
e8f0d548 708 def_dst = trace.type.data_stream_type(dst_name)
4810b707
PP
709
710 # create clock type C type mapping
711 clk_types_node = self._trace_type_node.get('clock-types')
712 clk_type_c_types = None
713
714 if clk_types_node is not None:
715 clk_type_c_types = collections.OrderedDict()
716
e8f0d548
PP
717 for dst in trace.type.data_stream_types:
718 if dst.default_clock_type is None:
4810b707
PP
719 continue
720
e8f0d548 721 clk_type_node = clk_types_node[dst.default_clock_type.name]
4810b707
PP
722 c_type = clk_type_node.get('$c-type')
723
724 if c_type is not None:
e8f0d548 725 clk_type_c_types[dst.default_clock_type] = c_type
4810b707
PP
726
727 # create options
728 iden_prefix_def = False
e8f0d548 729 def_dst_name_def = False
2d55dc7d
PP
730 opts_node = self.config_node.get('options')
731 iden_prefix = 'barectf_'
732 file_name_prefix = 'barectf'
4810b707
PP
733
734 if opts_node is not None:
735 code_gen_opts_node = opts_node.get('code-generation')
736
737 if code_gen_opts_node is not None:
738 prefix_node = code_gen_opts_node.get('prefix', 'barectf')
739
740 if type(prefix_node) is str:
741 # automatic prefixes
742 iden_prefix = f'{prefix_node}_'
743 file_name_prefix = prefix_node
744 else:
745 iden_prefix = prefix_node['identifier']
746 file_name_prefix = prefix_node['file-name']
747
748 header_opts = code_gen_opts_node.get('header')
749
750 if header_opts is not None:
751 iden_prefix_def = header_opts.get('identifier-prefix-definition', False)
e8f0d548
PP
752 def_dst_name_def = header_opts.get('default-data-stream-type-name-definition',
753 False)
4810b707
PP
754
755 header_opts = barectf_config.ConfigurationCodeGenerationHeaderOptions(iden_prefix_def,
e8f0d548 756 def_dst_name_def)
4810b707 757 cg_opts = barectf_config.ConfigurationCodeGenerationOptions(iden_prefix, file_name_prefix,
e8f0d548 758 def_dst, header_opts,
4810b707
PP
759 clk_type_c_types)
760 opts = barectf_config.ConfigurationOptions(cg_opts)
761
762 # create configuration
b2c06e26 763 self._config = barectf_config.Configuration(trace, opts)
4810b707
PP
764
765 # Expands the field type aliases found in the trace type node.
766 #
767 # This method modifies the trace type node.
768 #
769 # When this method returns:
770 #
771 # * Any field type alias is replaced with its full field type
772 # node equivalent.
773 #
774 # * The `$field-type-aliases` property of the trace type node is
775 # removed.
776 def _expand_ft_aliases(self):
2d55dc7d 777 def resolve_ft_alias_from(parent_node: _MapNode, key: str):
4810b707
PP
778 if key not in parent_node:
779 return
780
781 if type(parent_node[key]) not in [collections.OrderedDict, str]:
782 return
783
2d55dc7d 784 self._resolve_ft_alias_from(ft_aliases_node, parent_node, key)
4810b707
PP
785
786 ft_aliases_node = self._trace_type_node['$field-type-aliases']
787
e8f0d548
PP
788 # Expand field type aliases within trace, data stream, and event
789 # record type nodes.
4810b707
PP
790 features_prop_name = '$features'
791
792 try:
793 features_node = self._trace_type_node.get(features_prop_name)
794
795 if features_node is not None:
796 try:
797 resolve_ft_alias_from(features_node, 'magic-field-type')
798 resolve_ft_alias_from(features_node, 'uuid-field-type')
e8f0d548 799 resolve_ft_alias_from(features_node, 'data-stream-type-id-field-type')
4810b707
PP
800 except _ConfigurationParseError as exc:
801 _append_error_ctx(exc, f'`{features_prop_name}` property')
802 except _ConfigurationParseError as exc:
803 _append_error_ctx(exc, 'Trace type')
804
e8f0d548 805 for dst_name, dst_node in self._trace_type_node['data-stream-types'].items():
4810b707 806 try:
e8f0d548 807 features_node = dst_node.get(features_prop_name)
4810b707
PP
808
809 if features_node is not None:
810 try:
811 pkt_prop_name = 'packet'
812 pkt_node = features_node.get(pkt_prop_name)
813
814 if pkt_node is not None:
815 try:
816 resolve_ft_alias_from(pkt_node, 'total-size-field-type')
817 resolve_ft_alias_from(pkt_node, 'content-size-field-type')
462e49b3
PP
818 resolve_ft_alias_from(pkt_node, 'beginning-timestamp-field-type')
819 resolve_ft_alias_from(pkt_node, 'end-timestamp-field-type')
4810b707 820 resolve_ft_alias_from(pkt_node,
e8f0d548 821 'discarded-event-records-counter-snapshot-field-type')
af09c4fc 822 resolve_ft_alias_from(pkt_node, 'sequence-number-field-type')
4810b707
PP
823 except _ConfigurationParseError as exc:
824 _append_error_ctx(exc, f'`{pkt_prop_name}` property')
825
e8f0d548
PP
826 er_prop_name = 'event-record'
827 er_node = features_node.get(er_prop_name)
4810b707 828
e8f0d548 829 if er_node is not None:
4810b707 830 try:
e8f0d548 831 resolve_ft_alias_from(er_node, 'type-id-field-type')
462e49b3 832 resolve_ft_alias_from(er_node, 'timestamp-field-type')
4810b707 833 except _ConfigurationParseError as exc:
e8f0d548 834 _append_error_ctx(exc, f'`{er_prop_name}` property')
4810b707
PP
835 except _ConfigurationParseError as exc:
836 _append_error_ctx(exc, f'`{features_prop_name}` property')
837
838 pkt_ctx_ft_extra_members_prop_name = 'packet-context-field-type-extra-members'
e8f0d548 839 pkt_ctx_ft_extra_members_node = dst_node.get(pkt_ctx_ft_extra_members_prop_name)
4810b707
PP
840
841 if pkt_ctx_ft_extra_members_node is not None:
842 try:
843 for member_node in pkt_ctx_ft_extra_members_node:
844 member_node = list(member_node.values())[0]
845 resolve_ft_alias_from(member_node, 'field-type')
846 except _ConfigurationParseError as exc:
847 _append_error_ctx(exc, f'`{pkt_ctx_ft_extra_members_prop_name}` property')
848
e8f0d548 849 resolve_ft_alias_from(dst_node, 'event-record-common-context-field-type')
4810b707 850
e8f0d548 851 for ert_name, ert_node in dst_node['event-record-types'].items():
4810b707 852 try:
e8f0d548
PP
853 resolve_ft_alias_from(ert_node, 'specific-context-field-type')
854 resolve_ft_alias_from(ert_node, 'payload-field-type')
4810b707 855 except _ConfigurationParseError as exc:
e8f0d548 856 _append_error_ctx(exc, f'Event record type `{ert_name}`')
4810b707 857 except _ConfigurationParseError as exc:
e8f0d548 858 _append_error_ctx(exc, f'Data stream type `{dst_name}`')
4810b707
PP
859
860 # remove the (now unneeded) `$field-type-aliases` property
861 del self._trace_type_node['$field-type-aliases']
862
863 # Applies field type inheritance to all field type nodes found in
864 # the trace type node.
865 #
866 # This method modifies the trace type node.
867 #
868 # When this method returns, no field type node has an `$inherit`
869 # property.
870 def _apply_fts_inheritance(self):
2d55dc7d 871 def apply_ft_inheritance(parent_node: _MapNode, key: str):
4810b707
PP
872 if key not in parent_node:
873 return
874
875 if type(parent_node[key]) is not collections.OrderedDict:
876 return
877
2d55dc7d 878 self._apply_ft_inheritance(parent_node, key)
4810b707
PP
879
880 features_prop_name = '$features'
881 features_node = self._trace_type_node.get(features_prop_name)
882
883 if features_node is not None:
884 apply_ft_inheritance(features_node, 'magic-field-type')
885 apply_ft_inheritance(features_node, 'uuid-field-type')
e8f0d548 886 apply_ft_inheritance(features_node, 'data-stream-type-id-field-type')
4810b707 887
e8f0d548
PP
888 for dst_node in self._trace_type_node['data-stream-types'].values():
889 features_node = dst_node.get(features_prop_name)
4810b707
PP
890
891 if features_node is not None:
892 pkt_node = features_node.get('packet')
893
894 if pkt_node is not None:
895 apply_ft_inheritance(pkt_node, 'total-size-field-type')
896 apply_ft_inheritance(pkt_node, 'content-size-field-type')
462e49b3
PP
897 apply_ft_inheritance(pkt_node, 'beginning-timestamp-field-type')
898 apply_ft_inheritance(pkt_node, 'end-timestamp-field-type')
899 apply_ft_inheritance(pkt_node,
900 'discarded-event-records-counter-snapshot-field-type')
af09c4fc 901 apply_ft_inheritance(pkt_node, 'sequence-number-field-type')
4810b707 902
e8f0d548 903 er_node = features_node.get('event-record')
4810b707 904
e8f0d548
PP
905 if er_node is not None:
906 apply_ft_inheritance(er_node, 'type-id-field-type')
462e49b3 907 apply_ft_inheritance(er_node, 'timestamp-field-type')
4810b707 908
e8f0d548 909 pkt_ctx_ft_extra_members_node = dst_node.get('packet-context-field-type-extra-members')
4810b707
PP
910
911 if pkt_ctx_ft_extra_members_node is not None:
912 for member_node in pkt_ctx_ft_extra_members_node:
913 member_node = list(member_node.values())[0]
914 apply_ft_inheritance(member_node, 'field-type')
915
e8f0d548 916 apply_ft_inheritance(dst_node, 'event-record-common-context-field-type')
4810b707 917
e8f0d548
PP
918 for ert_node in dst_node['event-record-types'].values():
919 apply_ft_inheritance(ert_node, 'specific-context-field-type')
920 apply_ft_inheritance(ert_node, 'payload-field-type')
4810b707
PP
921
922 # Normalizes structure field type member nodes.
923 #
924 # A structure field type member node can look like this:
925 #
926 # - msg: custom-string
927 #
928 # which is the equivalent of this:
929 #
930 # - msg:
931 # field-type: custom-string
932 #
933 # This method normalizes form 1 to use form 2.
934 def _normalize_struct_ft_member_nodes(self):
2d55dc7d 935 def normalize_members_node(members_node: List[_MapNode]):
4810b707
PP
936 ft_prop_name = 'field-type'
937
938 for member_node in members_node:
939 member_name, val_node = list(member_node.items())[0]
940
941 if type(val_node) is str:
942 member_node[member_name] = collections.OrderedDict({
943 ft_prop_name: val_node
944 })
945
946 normalize_struct_ft_member_nodes(member_node[member_name], ft_prop_name)
947
2d55dc7d 948 def normalize_struct_ft_member_nodes(parent_node: _MapNode, key: str):
4810b707
PP
949 if type(parent_node) is not collections.OrderedDict:
950 return
951
952 ft_node = parent_node.get(key)
953
954 if type(ft_node) is not collections.OrderedDict:
955 return
956
2d55dc7d 957 ft_node = typing.cast(collections.OrderedDict, ft_node)
4810b707
PP
958 members_nodes = ft_node.get('members')
959
960 if members_nodes is not None:
961 normalize_members_node(members_nodes)
962
963 prop_name = '$field-type-aliases'
964 ft_aliases_node = self._trace_type_node.get(prop_name)
965
966 if ft_aliases_node is not None:
967 for alias in ft_aliases_node:
968 normalize_struct_ft_member_nodes(ft_aliases_node, alias)
969
970 features_prop_name = '$features'
971 features_node = self._trace_type_node.get(features_prop_name)
972
973 if features_node is not None:
974 normalize_struct_ft_member_nodes(features_node, 'magic-field-type')
975 normalize_struct_ft_member_nodes(features_node, 'uuid-field-type')
e8f0d548 976 normalize_struct_ft_member_nodes(features_node, 'data-stream-type-id-field-type')
4810b707 977
e8f0d548
PP
978 for dst_node in self._trace_type_node['data-stream-types'].values():
979 features_node = dst_node.get(features_prop_name)
4810b707
PP
980
981 if features_node is not None:
982 pkt_node = features_node.get('packet')
983
984 if pkt_node is not None:
985 normalize_struct_ft_member_nodes(pkt_node, 'total-size-field-type')
986 normalize_struct_ft_member_nodes(pkt_node, 'content-size-field-type')
462e49b3
PP
987 normalize_struct_ft_member_nodes(pkt_node, 'beginning-timestamp-field-type')
988 normalize_struct_ft_member_nodes(pkt_node, 'end-timestamp-field-type')
4810b707 989 normalize_struct_ft_member_nodes(pkt_node,
e8f0d548 990 'discarded-event-records-counter-snapshot-field-type')
af09c4fc 991 normalize_struct_ft_member_nodes(pkt_node, 'sequence-number-field-type')
4810b707 992
e8f0d548 993 er_node = features_node.get('event-record')
4810b707 994
e8f0d548
PP
995 if er_node is not None:
996 normalize_struct_ft_member_nodes(er_node, 'type-id-field-type')
462e49b3 997 normalize_struct_ft_member_nodes(er_node, 'timestamp-field-type')
4810b707 998
e8f0d548 999 pkt_ctx_ft_extra_members_node = dst_node.get('packet-context-field-type-extra-members')
4810b707
PP
1000
1001 if pkt_ctx_ft_extra_members_node is not None:
1002 normalize_members_node(pkt_ctx_ft_extra_members_node)
1003
e8f0d548 1004 normalize_struct_ft_member_nodes(dst_node, 'event-record-common-context-field-type')
4810b707 1005
e8f0d548
PP
1006 for ert_node in dst_node['event-record-types'].values():
1007 normalize_struct_ft_member_nodes(ert_node, 'specific-context-field-type')
1008 normalize_struct_ft_member_nodes(ert_node, 'payload-field-type')
4810b707
PP
1009
1010 # Calls _expand_ft_aliases() and _apply_fts_inheritance() if the
1011 # trace type node has a `$field-type-aliases` property.
1012 def _expand_fts(self):
1013 # Make sure that the current configuration node is valid
1014 # considering field types are not expanded yet.
2d55dc7d 1015 self._schema_validator.validate(self.config_node,
c3fa1a14 1016 'config/3/config-pre-field-type-expansion')
4810b707
PP
1017
1018 prop_name = '$field-type-aliases'
1019 ft_aliases_node = self._trace_type_node.get(prop_name)
1020
1021 if ft_aliases_node is None:
1022 # If there's no `'$field-type-aliases'` node, then there's
1023 # no field type aliases and therefore no possible
1024 # inheritance.
1025 if prop_name in self._trace_type_node:
1026 del self._trace_type_node[prop_name]
1027
1028 return
1029
1030 # normalize structure field type member nodes
1031 self._normalize_struct_ft_member_nodes()
1032
1033 # first, expand field type aliases
1034 self._expand_ft_aliases()
1035
1036 # next, apply inheritance to create effective field type nodes
1037 self._apply_fts_inheritance()
1038
e8f0d548 1039 # Substitute the event record type node log level aliases with their
4810b707
PP
1040 # numeric equivalents.
1041 #
1042 # Removes the `$log-level-aliases` property of the trace type node.
1043 def _sub_log_level_aliases(self):
1044 # Make sure that the current configuration node is valid
1045 # considering log level aliases are not substituted yet.
2d55dc7d 1046 self._schema_validator.validate(self.config_node,
c3fa1a14 1047 'config/3/config-pre-log-level-alias-sub')
4810b707
PP
1048
1049 log_level_aliases_prop_name = '$log-level-aliases'
1050 log_level_aliases_node = self._trace_type_node.get(log_level_aliases_prop_name)
1051
1052 if log_level_aliases_prop_name in self._trace_type_node:
1053 del self._trace_type_node[log_level_aliases_prop_name]
1054
1055 if log_level_aliases_node is None:
1056 # no log level aliases
1057 return
1058
1059 # substitute log level aliases
e8f0d548 1060 for dst_name, dst_node in self._trace_type_node['data-stream-types'].items():
4810b707 1061 try:
e8f0d548 1062 for ert_name, ert_node in dst_node['event-record-types'].items():
4810b707
PP
1063 try:
1064 prop_name = 'log-level'
e8f0d548 1065 ll_node = ert_node.get(prop_name)
4810b707
PP
1066
1067 if ll_node is None:
1068 continue
1069
1070 if type(ll_node) is str:
1071 if ll_node not in log_level_aliases_node:
1072 raise _ConfigurationParseError(f'`{prop_name}` property',
1073 f'Log level alias `{ll_node}` does not exist')
1074
e8f0d548 1075 ert_node[prop_name] = log_level_aliases_node[ll_node]
4810b707 1076 except _ConfigurationParseError as exc:
e8f0d548 1077 _append_error_ctx(exc, f'Event record type `{ert_name}`')
4810b707 1078 except _ConfigurationParseError as exc:
e8f0d548 1079 _append_error_ctx(exc, f'Data stream type `{dst_name}`')
4810b707
PP
1080
1081 # Generator of parent node and key pairs for all the nodes,
1082 # recursively, of `node`.
1083 #
1084 # It is safe to delete a yielded node during the iteration.
1085 @staticmethod
2d55dc7d 1086 def _props(node: Any) -> Iterable[Tuple[Any, str]]:
4810b707
PP
1087 if type(node) is collections.OrderedDict:
1088 for key in list(node):
1089 yield from _Parser._props(node[key])
1090 yield node, key
1091 elif type(node) is list:
1092 for item_node in node:
1093 yield from _Parser._props(item_node)
1094
2d55dc7d
PP
1095 def _trace_type_props(self) -> Iterable[Tuple[Any, str]]:
1096 yield from _Parser._props(self.config_node['trace']['type'])
4810b707 1097
a209cf4d
EB
1098 def _set_trace_byte_order_prop_key(self):
1099 if 'native-byte-order' in self._trace_type_node:
1100 self._trace_byte_order_prop_key = 'native-byte-order'
1101 else:
1102 self._trace_byte_order_prop_key = 'trace-byte-order'
1103
4810b707
PP
1104 # Normalize the properties of the configuration node.
1105 #
1106 # This method, for each property of the trace type node:
1107 #
1108 # 1. Removes it if it's `None` (means default).
1109 #
1110 # 2. Chooses a specific `class` property value.
1111 #
a209cf4d 1112 # 3. Chooses a specific trace byte order property value.
4810b707
PP
1113 #
1114 # 4. Chooses a specific `preferred-display-base` property value.
1115 #
1116 # This method also applies 1. to the trace node's `environment`
1117 # property.
1118 def _normalize_props(self):
2d55dc7d 1119 def normalize_byte_order_prop(parent_node: _MapNode, key: str):
4810b707
PP
1120 node = parent_node[key]
1121
1122 if node in ['be', 'big']:
1123 parent_node[key] = 'big-endian'
1124 elif node in ['le', 'little']:
1125 parent_node[key] = 'little-endian'
1126
2d55dc7d 1127 trace_node = self.config_node['trace']
a209cf4d 1128 normalize_byte_order_prop(self._trace_type_node, self._trace_byte_order_prop_key)
4810b707
PP
1129
1130 for parent_node, key in self._trace_type_props():
1131 node = parent_node[key]
1132
1133 if node is None:
1134 # a `None` property is equivalent to not having it
1135 del parent_node[key]
1136 continue
1137
1138 if key == 'class' and type(node) is str:
1139 # field type class aliases
1140 if node in ['uint', 'unsigned-int']:
1141 parent_node[key] = 'unsigned-integer'
1142 elif node in ['sint', 'signed-int']:
1143 parent_node[key] = 'signed-integer'
1144 elif node in ['uenum', 'unsigned-enum']:
1145 parent_node[key] = 'unsigned-enumeration'
1146 elif node in ['senum', 'signed-enum']:
1147 parent_node[key] = 'signed-enumeration'
1148 elif node == 'str':
1149 parent_node[key] = 'string'
1150 elif node == 'struct':
1151 parent_node[key] = 'structure'
4810b707
PP
1152 elif key == 'preferred-display-base' and type(node) is str:
1153 # display base aliases
1154 if node == 'bin':
1155 parent_node[key] = 'binary'
1156 elif node == 'oct':
1157 parent_node[key] = 'octal'
1158 elif node == 'dec':
1159 parent_node[key] = 'decimal'
1160 elif node == 'hex':
1161 parent_node[key] = 'hexadecimal'
1162
1163 prop_name = 'environment'
1164
1165 if prop_name in trace_node:
1166 node = trace_node[prop_name]
1167
1168 if node is None:
1169 del trace_node[prop_name]
1170
a209cf4d
EB
1171 # Sets the parser's trace byte order.
1172 def _set_trace_byte_order(self):
1173 self._trace_byte_order_node = self._trace_type_node[self._trace_byte_order_prop_key]
1174 self._trace_byte_order = self._byte_order_from_node(self._trace_byte_order_node)
4810b707 1175
e8f0d548
PP
1176 # Processes the inclusions of the event record type node
1177 # `ert_node`, returning the effective node.
1178 def _process_ert_node_include(self, ert_node: _MapNode) -> _MapNode:
1179 # Make sure the event record type node is valid for the
1180 # inclusion processing stage.
1181 self._schema_validator.validate(ert_node, 'config/3/ert-pre-include')
4810b707
PP
1182
1183 # process inclusions
e8f0d548 1184 return self._process_node_include(ert_node, self._process_ert_node_include)
4810b707 1185
e8f0d548
PP
1186 # Processes the inclusions of the data stream type node `dst_node`,
1187 # returning the effective node.
1188 def _process_dst_node_include(self, dst_node: _MapNode) -> _MapNode:
1189 def process_children_include(dst_node: _MapNode):
1190 prop_name = 'event-record-types'
4810b707 1191
e8f0d548
PP
1192 if prop_name in dst_node:
1193 erts_node = dst_node[prop_name]
4810b707 1194
e8f0d548
PP
1195 for key in list(erts_node):
1196 erts_node[key] = self._process_ert_node_include(erts_node[key])
4810b707 1197
e8f0d548 1198 # Make sure the data stream type node is valid for the inclusion
4810b707 1199 # processing stage.
e8f0d548 1200 self._schema_validator.validate(dst_node, 'config/3/dst-pre-include')
4810b707
PP
1201
1202 # process inclusions
e8f0d548 1203 return self._process_node_include(dst_node, self._process_dst_node_include,
4810b707
PP
1204 process_children_include)
1205
1206 # Processes the inclusions of the clock type node `clk_type_node`,
1207 # returning the effective node.
2d55dc7d 1208 def _process_clk_type_node_include(self, clk_type_node: _MapNode) -> _MapNode:
4810b707
PP
1209 # Make sure the clock type node is valid for the inclusion
1210 # processing stage.
c3fa1a14 1211 self._schema_validator.validate(clk_type_node, 'config/3/clock-type-pre-include')
4810b707
PP
1212
1213 # process inclusions
1214 return self._process_node_include(clk_type_node, self._process_clk_type_node_include)
1215
1216 # Processes the inclusions of the trace type node `trace_type_node`,
1217 # returning the effective node.
2d55dc7d
PP
1218 def _process_trace_type_node_include(self, trace_type_node: _MapNode) -> _MapNode:
1219 def process_children_include(trace_type_node: _MapNode):
4810b707
PP
1220 prop_name = 'clock-types'
1221
1222 if prop_name in trace_type_node:
1223 clk_types_node = trace_type_node[prop_name]
1224
1225 for key in list(clk_types_node):
1226 clk_types_node[key] = self._process_clk_type_node_include(clk_types_node[key])
1227
e8f0d548 1228 prop_name = 'data-stream-types'
4810b707
PP
1229
1230 if prop_name in trace_type_node:
e8f0d548 1231 dsts_node = trace_type_node[prop_name]
4810b707 1232
e8f0d548
PP
1233 for key in list(dsts_node):
1234 dsts_node[key] = self._process_dst_node_include(dsts_node[key])
4810b707
PP
1235
1236 # Make sure the trace type node is valid for the inclusion
1237 # processing stage.
c3fa1a14 1238 self._schema_validator.validate(trace_type_node, 'config/3/trace-type-pre-include')
4810b707
PP
1239
1240 # process inclusions
1241 return self._process_node_include(trace_type_node, self._process_trace_type_node_include,
1242 process_children_include)
1243
1244 # Processes the inclusions of the trace node `trace_node`, returning
1245 # the effective node.
2d55dc7d
PP
1246 def _process_trace_node_include(self, trace_node: _MapNode) -> _MapNode:
1247 def process_children_include(trace_node: _MapNode):
4810b707 1248 prop_name = 'type'
d51033ee
PP
1249
1250 if prop_name in trace_node:
1251 trace_node[prop_name] = self._process_trace_type_node_include(trace_node[prop_name])
4810b707
PP
1252
1253 # Make sure the trace node is valid for the inclusion processing
1254 # stage.
c3fa1a14 1255 self._schema_validator.validate(trace_node, 'config/3/trace-pre-include')
4810b707
PP
1256
1257 # process inclusions
1258 return self._process_node_include(trace_node, self._process_trace_node_include,
1259 process_children_include)
1260
1261 # Processes the inclusions of the configuration node, modifying it
1262 # during the process.
1263 def _process_config_includes(self):
1264 # Process inclusions in this order:
1265 #
e8f0d548
PP
1266 # 1. Clock type node and event record type nodes (the order
1267 # between those is not important).
4810b707 1268 #
e8f0d548 1269 # 2. Data stream type nodes.
4810b707
PP
1270 #
1271 # 3. Trace type node.
1272 #
1273 # 4. Trace node.
1274 #
1275 # This is because:
1276 #
1277 # * A trace node can include a trace type node, clock type
e8f0d548 1278 # nodes, data stream type nodes, and event record type nodes.
4810b707 1279 #
e8f0d548
PP
1280 # * A trace type node can include clock type nodes, data stream
1281 # type nodes, and event record type nodes.
4810b707 1282 #
e8f0d548 1283 # * A data stream type node can include event record type nodes.
4810b707
PP
1284 #
1285 # First, make sure the configuration node itself is valid for
1286 # the inclusion processing stage.
c3fa1a14 1287 self._schema_validator.validate(self.config_node, 'config/3/config-pre-include')
4810b707
PP
1288
1289 # Process trace node inclusions.
1290 #
1291 # self._process_trace_node_include() returns a new (or the same)
1292 # trace node without any `$include` property in it, recursively.
2d55dc7d 1293 self.config_node['trace'] = self._process_trace_node_include(self.config_node['trace'])
4810b707
PP
1294
1295 def _parse(self):
1296 # process configuration node inclusions
1297 self._process_config_includes()
1298
1299 # Expand field type nodes.
1300 #
1301 # This process:
1302 #
1303 # 1. Replaces field type aliases with "effective" field type
1304 # nodes, recursively.
1305 #
1306 # After this step, the `$field-type-aliases` property of the
1307 # trace type node is gone.
1308 #
1309 # 2. Applies inheritance, following the `$inherit` properties.
1310 #
1311 # After this step, field type nodes do not contain `$inherit`
1312 # properties.
1313 #
1314 # This is done blindly, in that the process _doesn't_ validate
1315 # field type nodes at this point.
1316 self._expand_fts()
1317
1318 # Substitute log level aliases.
1319 #
1320 # This process:
1321 #
e8f0d548
PP
1322 # 1. Replaces log level aliases in event record type nodes with
1323 # their numeric equivalents as found in the
1324 # `$log-level-aliases` property of the trace type node.
4810b707
PP
1325 #
1326 # 2. Removes the `$log-level-aliases` property from the trace
1327 # type node.
1328 self._sub_log_level_aliases()
1329
1330 # At this point, the configuration node must be valid as an
1331 # effective configuration node.
c3fa1a14 1332 self._schema_validator.validate(self.config_node, 'config/3/config')
4810b707 1333
a209cf4d
EB
1334 # Set the trace byte order property key.
1335 self._set_trace_byte_order_prop_key()
1336
4810b707
PP
1337 # Normalize properties.
1338 #
1339 # This process removes `None` properties and chooses specific
1340 # enumerators when aliases exist (for example, `big-endian`
1341 # instead of `be`).
1342 #
1343 # The goal of this is that, if the user then gets this parser's
1344 # `config_node` property, it has a normal and very readable
1345 # form.
1346 #
1347 # It also makes _create_config() easier to implement because it
1348 # doesn't need to check for `None` nodes or enumerator aliases.
1349 self._normalize_props()
1350
a209cf4d
EB
1351 # Set the trace byte order.
1352 self._set_trace_byte_order()
4810b707
PP
1353
1354 # Create a barectf configuration object from the configuration
1355 # node.
1356 self._create_config()
1357
1358 @property
2d55dc7d 1359 def config(self) -> barectf_config.Configuration:
4810b707
PP
1360 return self._config
1361
1362 @property
2d55dc7d
PP
1363 def config_node(self) -> _MapNode:
1364 return typing.cast(barectf_config_parse_common._ConfigNodeV3, self._root_node).config_node
This page took 0.090151 seconds and 4 git commands to generate.