barectf.c.j2: only declare `ts` variable when needed
[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
4810b707
PP
441
442 if def_clk_type is not None:
e8f0d548 443 # The data stream type has a default clock type.
462e49b3
PP
444 # Initialize the packet beginning timestamp, packet end
445 # timestamp, and event record timestamp field types to
446 # default field types.
4810b707 447 #
e8f0d548
PP
448 # This means your data stream type node only needs a
449 # default clock type name to enable those features
4810b707 450 # automatically. Those features do not add any parameter
e8f0d548 451 # to the event tracing functions.
462e49b3
PP
452 pkt_beginning_ts_ft = barectf_config.DEFAULT_FIELD_TYPE
453 pkt_end_ts_ft = barectf_config.DEFAULT_FIELD_TYPE
454 ert_ts_ft = barectf_config.DEFAULT_FIELD_TYPE
4810b707 455
e8f0d548 456 features_node = dst_node.get('$features')
4810b707
PP
457
458 if features_node is not None:
459 # create packet feature field types
460 pkt_node = features_node.get('packet')
461
462 if pkt_node is not None:
463 pkt_total_size_ft = self._feature_ft(pkt_node, 'total-size-field-type',
464 pkt_total_size_ft)
465 pkt_content_size_ft = self._feature_ft(pkt_node, 'content-size-field-type',
466 pkt_content_size_ft)
462e49b3
PP
467 pkt_beginning_ts_ft = self._feature_ft(pkt_node,
468 'beginning-timestamp-field-type',
469 pkt_beginning_ts_ft)
470 pkt_end_ts_ft = self._feature_ft(pkt_node, 'end-timestamp-field-type',
471 pkt_end_ts_ft)
470ff4d9 472 pkt_disc_er_counter_snap_ft = self._feature_ft(pkt_node,
e8f0d548 473 'discarded-event-records-counter-snapshot-field-type',
470ff4d9 474 pkt_disc_er_counter_snap_ft)
4810b707 475
e8f0d548
PP
476 # create event record feature field types
477 er_node = features_node.get('event-record')
4810b707
PP
478 type_id_ft_prop_name = 'type-id-field-type'
479
e8f0d548
PP
480 if er_node is not None:
481 ert_id_ft = self._feature_ft(er_node, type_id_ft_prop_name, ert_id_ft)
462e49b3 482 ert_ts_ft = self._feature_ft(er_node, 'timestamp-field-type', ert_ts_ft)
4810b707 483
e8f0d548
PP
484 erts_prop_name = 'event-record-types'
485 ert_count = len(dst_node[erts_prop_name])
4810b707
PP
486
487 try:
e8f0d548 488 if ert_id_ft is None and ert_count > 1:
4810b707 489 raise _ConfigurationParseError(f'`{type_id_ft_prop_name}` property',
e8f0d548 490 'Event record type ID field type feature is required because data stream type has more than one event record type')
4810b707 491
e8f0d548
PP
492 if isinstance(ert_id_ft, barectf_config._IntegerFieldType):
493 ert_id_int_ft = typing.cast(barectf_config._IntegerFieldType, ert_id_ft)
2d55dc7d 494
e8f0d548 495 if ert_count > (1 << ert_id_int_ft.size):
2d55dc7d 496 raise _ConfigurationParseError(f'`{type_id_ft_prop_name}` property',
e8f0d548 497 f'Field type\'s size ({ert_id_int_ft.size} bits) is too small to accomodate {ert_count} event record types')
4810b707 498 except _ConfigurationParseError as exc:
e8f0d548 499 exc._append_ctx('`event-record` property')
4810b707
PP
500 _append_error_ctx(exc, '`$features` property')
501
e8f0d548
PP
502 pkt_features = barectf_config.DataStreamTypePacketFeatures(pkt_total_size_ft,
503 pkt_content_size_ft,
462e49b3
PP
504 pkt_beginning_ts_ft,
505 pkt_end_ts_ft,
470ff4d9 506 pkt_disc_er_counter_snap_ft)
462e49b3 507 er_features = barectf_config.DataStreamTypeEventRecordFeatures(ert_id_ft, ert_ts_ft)
e8f0d548 508 features = barectf_config.DataStreamTypeFeatures(pkt_features, er_features)
4810b707
PP
509
510 # create packet context (structure) field type extra members
511 pkt_ctx_ft_extra_members = None
512 prop_name = 'packet-context-field-type-extra-members'
e8f0d548 513 pkt_ctx_ft_extra_members_node = dst_node.get(prop_name)
4810b707
PP
514
515 if pkt_ctx_ft_extra_members_node is not None:
516 pkt_ctx_ft_extra_members = self._create_struct_ft_members(pkt_ctx_ft_extra_members_node,
517 prop_name)
518
519 # check for illegal packet context field type member names
520 reserved_member_names = {
521 'packet_size',
522 'content_size',
523 'timestamp_begin',
524 'timestamp_end',
525 'events_discarded',
526 'packet_seq_num',
527 }
528
529 for member_name in pkt_ctx_ft_extra_members:
530 if member_name in reserved_member_names:
531 raise _ConfigurationParseError(f'`{prop_name}` property',
532 f'Packet context field type member name `{member_name}` is reserved.')
533
e8f0d548
PP
534 # create event record types
535 er_header_common_ctx_member_count = Count(0)
4810b707 536
e8f0d548
PP
537 if er_features.type_id_field_type is not None:
538 er_header_common_ctx_member_count = Count(er_header_common_ctx_member_count + 1)
4810b707 539
462e49b3 540 if er_features.timestamp_field_type is not None:
e8f0d548 541 er_header_common_ctx_member_count = Count(er_header_common_ctx_member_count + 1)
4810b707 542
e8f0d548
PP
543 er_common_ctx_ft_prop_name = 'event-record-common-context-field-type'
544 er_common_ctx_ft_node = dst_node.get(er_common_ctx_ft_prop_name)
545 er_header_common_ctx_member_count = Count(er_header_common_ctx_member_count + self._total_struct_ft_node_members(er_common_ctx_ft_node))
546 erts = set()
4810b707 547
e8f0d548
PP
548 for ert_name, ert_node in dst_node[erts_prop_name].items():
549 erts.add(self._create_ert(ert_name, ert_node, er_header_common_ctx_member_count))
4810b707 550
e8f0d548
PP
551 # create data stream type
552 return barectf_config.DataStreamType(name, erts, def_clk_type, features,
553 pkt_ctx_ft_extra_members,
554 self._try_create_struct_ft(dst_node,
555 er_common_ctx_ft_prop_name))
4810b707 556 except _ConfigurationParseError as exc:
e8f0d548 557 _append_error_ctx(exc, f'Data data stream type `{name}`')
4810b707 558
2d55dc7d
PP
559 # satisfy static type checker (never reached)
560 raise
561
562 def _clk_type(self, name: str, prop_name: str) -> barectf_config.ClockType:
4810b707
PP
563 clk_type = self._clk_types.get(name)
564
565 if clk_type is None:
566 raise _ConfigurationParseError(f'`{prop_name}` property',
567 f'Clock type `{name}` does not exist')
568
569 return clk_type
570
2d55dc7d 571 def _create_clk_type(self, name: str, clk_type_node: _MapNode) -> barectf_config.ClockType:
4810b707
PP
572 self._validate_iden(name, '`name` property', 'clock type name')
573 clk_type_uuid = None
574 uuid_node = clk_type_node.get('uuid')
575
576 if uuid_node is not None:
577 clk_type_uuid = uuid.UUID(uuid_node)
578
579 offset_seconds = 0
2d55dc7d 580 offset_cycles = Count(0)
4810b707
PP
581 offset_node = clk_type_node.get('offset')
582
583 if offset_node is not None:
584 offset_seconds = offset_node.get('seconds', 0)
2d55dc7d 585 offset_cycles = offset_node.get('cycles', Count(0))
4810b707
PP
586
587 return barectf_config.ClockType(name, clk_type_node.get('frequency', int(1e9)),
588 clk_type_uuid, clk_type_node.get('description'),
589 clk_type_node.get('precision', 0),
590 barectf_config.ClockTypeOffset(offset_seconds, offset_cycles),
591 clk_type_node.get('origin-is-unix-epoch', False))
592
593 def _create_clk_types(self):
594 self._clk_types = {}
595
596 for clk_type_name, clk_type_node in self._trace_type_node.get('clock-types', {}).items():
597 self._clk_types[clk_type_name] = self._create_clk_type(clk_type_name, clk_type_node)
598
599 def _create_trace_type(self):
600 try:
e8f0d548 601 # create clock types (_create_dst() needs them)
4810b707
PP
602 self._create_clk_types()
603
604 # get UUID
605 trace_type_uuid = None
606 uuid_node = self._trace_type_node.get('uuid')
607
608 if uuid_node is not None:
609 if uuid_node == 'auto':
610 trace_type_uuid = uuid.uuid1()
611 else:
612 trace_type_uuid = uuid.UUID(uuid_node)
613
614 # create feature field types
615 magic_ft = barectf_config.DEFAULT_FIELD_TYPE
616 uuid_ft = None
e8f0d548 617 dst_id_ft = barectf_config.DEFAULT_FIELD_TYPE
4810b707
PP
618
619 if trace_type_uuid is not None:
620 # Trace type has a UUID: initialize UUID field type to
621 # a default field type.
622 uuid_ft = barectf_config.DEFAULT_FIELD_TYPE
623
624 features_node = self._trace_type_node.get('$features')
e8f0d548 625 dst_id_ft_prop_name = 'data-stream-type-id-field-type'
4810b707
PP
626
627 if features_node is not None:
628 magic_ft = self._feature_ft(features_node, 'magic-field-type',
629 magic_ft)
630 uuid_ft = self._feature_ft(features_node, 'uuid-field-type', uuid_ft)
e8f0d548 631 dst_id_ft = self._feature_ft(features_node, dst_id_ft_prop_name, dst_id_ft)
4810b707 632
e8f0d548
PP
633 dsts_prop_name = 'data-stream-types'
634 dst_count = len(self._trace_type_node[dsts_prop_name])
4810b707
PP
635
636 try:
e8f0d548
PP
637 if dst_id_ft is None and dst_count > 1:
638 raise _ConfigurationParseError(f'`{dst_id_ft_prop_name}` property',
639 'Data stream type ID field type feature is required because trace type has more than one data stream type')
4810b707 640
e8f0d548
PP
641 if isinstance(dst_id_ft, barectf_config._FieldType) and dst_count > (1 << dst_id_ft.size):
642 raise _ConfigurationParseError(f'`{dst_id_ft_prop_name}` property',
643 f'Field type\'s size ({dst_id_ft.size} bits) is too small to accomodate {dst_count} data stream types')
4810b707
PP
644 except _ConfigurationParseError as exc:
645 _append_error_ctx(exc, '`$features` property')
646
e8f0d548 647 features = barectf_config.TraceTypeFeatures(magic_ft, uuid_ft, dst_id_ft)
4810b707 648
e8f0d548
PP
649 # create data stream types
650 dsts = set()
4810b707 651
e8f0d548
PP
652 for dst_name, dst_node in self._trace_type_node[dsts_prop_name].items():
653 dsts.add(self._create_dst(dst_name, dst_node))
4810b707
PP
654
655 # create trace type
313a0bd9 656 return barectf_config.TraceType(self._native_byte_order, dsts, trace_type_uuid,
b2c06e26 657 features)
4810b707
PP
658 except _ConfigurationParseError as exc:
659 _append_error_ctx(exc, 'Trace type')
660
661 def _create_trace(self):
662 try:
663 trace_type = self._create_trace_type()
2d55dc7d 664 trace_node = self.config_node['trace']
4810b707
PP
665 env = None
666 env_node = trace_node.get('environment')
667
668 if env_node is not None:
669 # validate each environment variable name
670 for name in env_node:
671 self._validate_iden(name, '`environment` property',
672 'environment variable name')
673
674 # the node already has the expected structure
675 env = barectf_config.TraceEnvironment(env_node)
676
677 return barectf_config.Trace(trace_type, env)
678
679 except _ConfigurationParseError as exc:
680 _append_error_ctx(exc, 'Trace')
681
682 def _create_config(self):
683 # create trace first
684 trace = self._create_trace()
685
e8f0d548
PP
686 # find default data stream type, if any
687 def_dst = None
4810b707 688
e8f0d548 689 for dst_name, dst_node in self._trace_type_node['data-stream-types'].items():
4810b707 690 prop_name = '$is-default'
e8f0d548 691 is_default = dst_node.get(prop_name)
4810b707
PP
692
693 if is_default is True:
e8f0d548 694 if def_dst is not None:
4810b707 695 exc = _ConfigurationParseError(f'`{prop_name}` property',
e8f0d548
PP
696 f'Duplicate default data stream type (`{def_dst.name}`)')
697 exc._append_ctx(f'Data stream type `{dst_name}`')
4810b707
PP
698 _append_error_ctx(exc, 'Trace type')
699
e8f0d548 700 def_dst = trace.type.data_stream_type(dst_name)
4810b707
PP
701
702 # create clock type C type mapping
703 clk_types_node = self._trace_type_node.get('clock-types')
704 clk_type_c_types = None
705
706 if clk_types_node is not None:
707 clk_type_c_types = collections.OrderedDict()
708
e8f0d548
PP
709 for dst in trace.type.data_stream_types:
710 if dst.default_clock_type is None:
4810b707
PP
711 continue
712
e8f0d548 713 clk_type_node = clk_types_node[dst.default_clock_type.name]
4810b707
PP
714 c_type = clk_type_node.get('$c-type')
715
716 if c_type is not None:
e8f0d548 717 clk_type_c_types[dst.default_clock_type] = c_type
4810b707
PP
718
719 # create options
720 iden_prefix_def = False
e8f0d548 721 def_dst_name_def = False
2d55dc7d
PP
722 opts_node = self.config_node.get('options')
723 iden_prefix = 'barectf_'
724 file_name_prefix = 'barectf'
4810b707
PP
725
726 if opts_node is not None:
727 code_gen_opts_node = opts_node.get('code-generation')
728
729 if code_gen_opts_node is not None:
730 prefix_node = code_gen_opts_node.get('prefix', 'barectf')
731
732 if type(prefix_node) is str:
733 # automatic prefixes
734 iden_prefix = f'{prefix_node}_'
735 file_name_prefix = prefix_node
736 else:
737 iden_prefix = prefix_node['identifier']
738 file_name_prefix = prefix_node['file-name']
739
740 header_opts = code_gen_opts_node.get('header')
741
742 if header_opts is not None:
743 iden_prefix_def = header_opts.get('identifier-prefix-definition', False)
e8f0d548
PP
744 def_dst_name_def = header_opts.get('default-data-stream-type-name-definition',
745 False)
4810b707
PP
746
747 header_opts = barectf_config.ConfigurationCodeGenerationHeaderOptions(iden_prefix_def,
e8f0d548 748 def_dst_name_def)
4810b707 749 cg_opts = barectf_config.ConfigurationCodeGenerationOptions(iden_prefix, file_name_prefix,
e8f0d548 750 def_dst, header_opts,
4810b707
PP
751 clk_type_c_types)
752 opts = barectf_config.ConfigurationOptions(cg_opts)
753
754 # create configuration
b2c06e26 755 self._config = barectf_config.Configuration(trace, opts)
4810b707
PP
756
757 # Expands the field type aliases found in the trace type node.
758 #
759 # This method modifies the trace type node.
760 #
761 # When this method returns:
762 #
763 # * Any field type alias is replaced with its full field type
764 # node equivalent.
765 #
766 # * The `$field-type-aliases` property of the trace type node is
767 # removed.
768 def _expand_ft_aliases(self):
2d55dc7d 769 def resolve_ft_alias_from(parent_node: _MapNode, key: str):
4810b707
PP
770 if key not in parent_node:
771 return
772
773 if type(parent_node[key]) not in [collections.OrderedDict, str]:
774 return
775
2d55dc7d 776 self._resolve_ft_alias_from(ft_aliases_node, parent_node, key)
4810b707
PP
777
778 ft_aliases_node = self._trace_type_node['$field-type-aliases']
779
e8f0d548
PP
780 # Expand field type aliases within trace, data stream, and event
781 # record type nodes.
4810b707
PP
782 features_prop_name = '$features'
783
784 try:
785 features_node = self._trace_type_node.get(features_prop_name)
786
787 if features_node is not None:
788 try:
789 resolve_ft_alias_from(features_node, 'magic-field-type')
790 resolve_ft_alias_from(features_node, 'uuid-field-type')
e8f0d548 791 resolve_ft_alias_from(features_node, 'data-stream-type-id-field-type')
4810b707
PP
792 except _ConfigurationParseError as exc:
793 _append_error_ctx(exc, f'`{features_prop_name}` property')
794 except _ConfigurationParseError as exc:
795 _append_error_ctx(exc, 'Trace type')
796
e8f0d548 797 for dst_name, dst_node in self._trace_type_node['data-stream-types'].items():
4810b707 798 try:
e8f0d548 799 features_node = dst_node.get(features_prop_name)
4810b707
PP
800
801 if features_node is not None:
802 try:
803 pkt_prop_name = 'packet'
804 pkt_node = features_node.get(pkt_prop_name)
805
806 if pkt_node is not None:
807 try:
808 resolve_ft_alias_from(pkt_node, 'total-size-field-type')
809 resolve_ft_alias_from(pkt_node, 'content-size-field-type')
462e49b3
PP
810 resolve_ft_alias_from(pkt_node, 'beginning-timestamp-field-type')
811 resolve_ft_alias_from(pkt_node, 'end-timestamp-field-type')
4810b707 812 resolve_ft_alias_from(pkt_node,
e8f0d548 813 'discarded-event-records-counter-snapshot-field-type')
4810b707
PP
814 except _ConfigurationParseError as exc:
815 _append_error_ctx(exc, f'`{pkt_prop_name}` property')
816
e8f0d548
PP
817 er_prop_name = 'event-record'
818 er_node = features_node.get(er_prop_name)
4810b707 819
e8f0d548 820 if er_node is not None:
4810b707 821 try:
e8f0d548 822 resolve_ft_alias_from(er_node, 'type-id-field-type')
462e49b3 823 resolve_ft_alias_from(er_node, 'timestamp-field-type')
4810b707 824 except _ConfigurationParseError as exc:
e8f0d548 825 _append_error_ctx(exc, f'`{er_prop_name}` property')
4810b707
PP
826 except _ConfigurationParseError as exc:
827 _append_error_ctx(exc, f'`{features_prop_name}` property')
828
829 pkt_ctx_ft_extra_members_prop_name = 'packet-context-field-type-extra-members'
e8f0d548 830 pkt_ctx_ft_extra_members_node = dst_node.get(pkt_ctx_ft_extra_members_prop_name)
4810b707
PP
831
832 if pkt_ctx_ft_extra_members_node is not None:
833 try:
834 for member_node in pkt_ctx_ft_extra_members_node:
835 member_node = list(member_node.values())[0]
836 resolve_ft_alias_from(member_node, 'field-type')
837 except _ConfigurationParseError as exc:
838 _append_error_ctx(exc, f'`{pkt_ctx_ft_extra_members_prop_name}` property')
839
e8f0d548 840 resolve_ft_alias_from(dst_node, 'event-record-common-context-field-type')
4810b707 841
e8f0d548 842 for ert_name, ert_node in dst_node['event-record-types'].items():
4810b707 843 try:
e8f0d548
PP
844 resolve_ft_alias_from(ert_node, 'specific-context-field-type')
845 resolve_ft_alias_from(ert_node, 'payload-field-type')
4810b707 846 except _ConfigurationParseError as exc:
e8f0d548 847 _append_error_ctx(exc, f'Event record type `{ert_name}`')
4810b707 848 except _ConfigurationParseError as exc:
e8f0d548 849 _append_error_ctx(exc, f'Data stream type `{dst_name}`')
4810b707
PP
850
851 # remove the (now unneeded) `$field-type-aliases` property
852 del self._trace_type_node['$field-type-aliases']
853
854 # Applies field type inheritance to all field type nodes found in
855 # the trace type node.
856 #
857 # This method modifies the trace type node.
858 #
859 # When this method returns, no field type node has an `$inherit`
860 # property.
861 def _apply_fts_inheritance(self):
2d55dc7d 862 def apply_ft_inheritance(parent_node: _MapNode, key: str):
4810b707
PP
863 if key not in parent_node:
864 return
865
866 if type(parent_node[key]) is not collections.OrderedDict:
867 return
868
2d55dc7d 869 self._apply_ft_inheritance(parent_node, key)
4810b707
PP
870
871 features_prop_name = '$features'
872 features_node = self._trace_type_node.get(features_prop_name)
873
874 if features_node is not None:
875 apply_ft_inheritance(features_node, 'magic-field-type')
876 apply_ft_inheritance(features_node, 'uuid-field-type')
e8f0d548 877 apply_ft_inheritance(features_node, 'data-stream-type-id-field-type')
4810b707 878
e8f0d548
PP
879 for dst_node in self._trace_type_node['data-stream-types'].values():
880 features_node = dst_node.get(features_prop_name)
4810b707
PP
881
882 if features_node is not None:
883 pkt_node = features_node.get('packet')
884
885 if pkt_node is not None:
886 apply_ft_inheritance(pkt_node, 'total-size-field-type')
887 apply_ft_inheritance(pkt_node, 'content-size-field-type')
462e49b3
PP
888 apply_ft_inheritance(pkt_node, 'beginning-timestamp-field-type')
889 apply_ft_inheritance(pkt_node, 'end-timestamp-field-type')
890 apply_ft_inheritance(pkt_node,
891 'discarded-event-records-counter-snapshot-field-type')
4810b707 892
e8f0d548 893 er_node = features_node.get('event-record')
4810b707 894
e8f0d548
PP
895 if er_node is not None:
896 apply_ft_inheritance(er_node, 'type-id-field-type')
462e49b3 897 apply_ft_inheritance(er_node, 'timestamp-field-type')
4810b707 898
e8f0d548 899 pkt_ctx_ft_extra_members_node = dst_node.get('packet-context-field-type-extra-members')
4810b707
PP
900
901 if pkt_ctx_ft_extra_members_node is not None:
902 for member_node in pkt_ctx_ft_extra_members_node:
903 member_node = list(member_node.values())[0]
904 apply_ft_inheritance(member_node, 'field-type')
905
e8f0d548 906 apply_ft_inheritance(dst_node, 'event-record-common-context-field-type')
4810b707 907
e8f0d548
PP
908 for ert_node in dst_node['event-record-types'].values():
909 apply_ft_inheritance(ert_node, 'specific-context-field-type')
910 apply_ft_inheritance(ert_node, 'payload-field-type')
4810b707
PP
911
912 # Normalizes structure field type member nodes.
913 #
914 # A structure field type member node can look like this:
915 #
916 # - msg: custom-string
917 #
918 # which is the equivalent of this:
919 #
920 # - msg:
921 # field-type: custom-string
922 #
923 # This method normalizes form 1 to use form 2.
924 def _normalize_struct_ft_member_nodes(self):
2d55dc7d 925 def normalize_members_node(members_node: List[_MapNode]):
4810b707
PP
926 ft_prop_name = 'field-type'
927
928 for member_node in members_node:
929 member_name, val_node = list(member_node.items())[0]
930
931 if type(val_node) is str:
932 member_node[member_name] = collections.OrderedDict({
933 ft_prop_name: val_node
934 })
935
936 normalize_struct_ft_member_nodes(member_node[member_name], ft_prop_name)
937
2d55dc7d 938 def normalize_struct_ft_member_nodes(parent_node: _MapNode, key: str):
4810b707
PP
939 if type(parent_node) is not collections.OrderedDict:
940 return
941
942 ft_node = parent_node.get(key)
943
944 if type(ft_node) is not collections.OrderedDict:
945 return
946
2d55dc7d 947 ft_node = typing.cast(collections.OrderedDict, ft_node)
4810b707
PP
948 members_nodes = ft_node.get('members')
949
950 if members_nodes is not None:
951 normalize_members_node(members_nodes)
952
953 prop_name = '$field-type-aliases'
954 ft_aliases_node = self._trace_type_node.get(prop_name)
955
956 if ft_aliases_node is not None:
957 for alias in ft_aliases_node:
958 normalize_struct_ft_member_nodes(ft_aliases_node, alias)
959
960 features_prop_name = '$features'
961 features_node = self._trace_type_node.get(features_prop_name)
962
963 if features_node is not None:
964 normalize_struct_ft_member_nodes(features_node, 'magic-field-type')
965 normalize_struct_ft_member_nodes(features_node, 'uuid-field-type')
e8f0d548 966 normalize_struct_ft_member_nodes(features_node, 'data-stream-type-id-field-type')
4810b707 967
e8f0d548
PP
968 for dst_node in self._trace_type_node['data-stream-types'].values():
969 features_node = dst_node.get(features_prop_name)
4810b707
PP
970
971 if features_node is not None:
972 pkt_node = features_node.get('packet')
973
974 if pkt_node is not None:
975 normalize_struct_ft_member_nodes(pkt_node, 'total-size-field-type')
976 normalize_struct_ft_member_nodes(pkt_node, 'content-size-field-type')
462e49b3
PP
977 normalize_struct_ft_member_nodes(pkt_node, 'beginning-timestamp-field-type')
978 normalize_struct_ft_member_nodes(pkt_node, 'end-timestamp-field-type')
4810b707 979 normalize_struct_ft_member_nodes(pkt_node,
e8f0d548 980 'discarded-event-records-counter-snapshot-field-type')
4810b707 981
e8f0d548 982 er_node = features_node.get('event-record')
4810b707 983
e8f0d548
PP
984 if er_node is not None:
985 normalize_struct_ft_member_nodes(er_node, 'type-id-field-type')
462e49b3 986 normalize_struct_ft_member_nodes(er_node, 'timestamp-field-type')
4810b707 987
e8f0d548 988 pkt_ctx_ft_extra_members_node = dst_node.get('packet-context-field-type-extra-members')
4810b707
PP
989
990 if pkt_ctx_ft_extra_members_node is not None:
991 normalize_members_node(pkt_ctx_ft_extra_members_node)
992
e8f0d548 993 normalize_struct_ft_member_nodes(dst_node, 'event-record-common-context-field-type')
4810b707 994
e8f0d548
PP
995 for ert_node in dst_node['event-record-types'].values():
996 normalize_struct_ft_member_nodes(ert_node, 'specific-context-field-type')
997 normalize_struct_ft_member_nodes(ert_node, 'payload-field-type')
4810b707
PP
998
999 # Calls _expand_ft_aliases() and _apply_fts_inheritance() if the
1000 # trace type node has a `$field-type-aliases` property.
1001 def _expand_fts(self):
1002 # Make sure that the current configuration node is valid
1003 # considering field types are not expanded yet.
2d55dc7d 1004 self._schema_validator.validate(self.config_node,
c3fa1a14 1005 'config/3/config-pre-field-type-expansion')
4810b707
PP
1006
1007 prop_name = '$field-type-aliases'
1008 ft_aliases_node = self._trace_type_node.get(prop_name)
1009
1010 if ft_aliases_node is None:
1011 # If there's no `'$field-type-aliases'` node, then there's
1012 # no field type aliases and therefore no possible
1013 # inheritance.
1014 if prop_name in self._trace_type_node:
1015 del self._trace_type_node[prop_name]
1016
1017 return
1018
1019 # normalize structure field type member nodes
1020 self._normalize_struct_ft_member_nodes()
1021
1022 # first, expand field type aliases
1023 self._expand_ft_aliases()
1024
1025 # next, apply inheritance to create effective field type nodes
1026 self._apply_fts_inheritance()
1027
e8f0d548 1028 # Substitute the event record type node log level aliases with their
4810b707
PP
1029 # numeric equivalents.
1030 #
1031 # Removes the `$log-level-aliases` property of the trace type node.
1032 def _sub_log_level_aliases(self):
1033 # Make sure that the current configuration node is valid
1034 # considering log level aliases are not substituted yet.
2d55dc7d 1035 self._schema_validator.validate(self.config_node,
c3fa1a14 1036 'config/3/config-pre-log-level-alias-sub')
4810b707
PP
1037
1038 log_level_aliases_prop_name = '$log-level-aliases'
1039 log_level_aliases_node = self._trace_type_node.get(log_level_aliases_prop_name)
1040
1041 if log_level_aliases_prop_name in self._trace_type_node:
1042 del self._trace_type_node[log_level_aliases_prop_name]
1043
1044 if log_level_aliases_node is None:
1045 # no log level aliases
1046 return
1047
1048 # substitute log level aliases
e8f0d548 1049 for dst_name, dst_node in self._trace_type_node['data-stream-types'].items():
4810b707 1050 try:
e8f0d548 1051 for ert_name, ert_node in dst_node['event-record-types'].items():
4810b707
PP
1052 try:
1053 prop_name = 'log-level'
e8f0d548 1054 ll_node = ert_node.get(prop_name)
4810b707
PP
1055
1056 if ll_node is None:
1057 continue
1058
1059 if type(ll_node) is str:
1060 if ll_node not in log_level_aliases_node:
1061 raise _ConfigurationParseError(f'`{prop_name}` property',
1062 f'Log level alias `{ll_node}` does not exist')
1063
e8f0d548 1064 ert_node[prop_name] = log_level_aliases_node[ll_node]
4810b707 1065 except _ConfigurationParseError as exc:
e8f0d548 1066 _append_error_ctx(exc, f'Event record type `{ert_name}`')
4810b707 1067 except _ConfigurationParseError as exc:
e8f0d548 1068 _append_error_ctx(exc, f'Data stream type `{dst_name}`')
4810b707
PP
1069
1070 # Generator of parent node and key pairs for all the nodes,
1071 # recursively, of `node`.
1072 #
1073 # It is safe to delete a yielded node during the iteration.
1074 @staticmethod
2d55dc7d 1075 def _props(node: Any) -> Iterable[Tuple[Any, str]]:
4810b707
PP
1076 if type(node) is collections.OrderedDict:
1077 for key in list(node):
1078 yield from _Parser._props(node[key])
1079 yield node, key
1080 elif type(node) is list:
1081 for item_node in node:
1082 yield from _Parser._props(item_node)
1083
2d55dc7d
PP
1084 def _trace_type_props(self) -> Iterable[Tuple[Any, str]]:
1085 yield from _Parser._props(self.config_node['trace']['type'])
4810b707
PP
1086
1087 # Normalize the properties of the configuration node.
1088 #
1089 # This method, for each property of the trace type node:
1090 #
1091 # 1. Removes it if it's `None` (means default).
1092 #
1093 # 2. Chooses a specific `class` property value.
1094 #
313a0bd9 1095 # 3. Chooses a specific `byte-order`/`native-byte-order` property
4810b707
PP
1096 # value.
1097 #
1098 # 4. Chooses a specific `preferred-display-base` property value.
1099 #
1100 # This method also applies 1. to the trace node's `environment`
1101 # property.
1102 def _normalize_props(self):
2d55dc7d 1103 def normalize_byte_order_prop(parent_node: _MapNode, key: str):
4810b707
PP
1104 node = parent_node[key]
1105
1106 if node in ['be', 'big']:
1107 parent_node[key] = 'big-endian'
1108 elif node in ['le', 'little']:
1109 parent_node[key] = 'little-endian'
1110
2d55dc7d 1111 trace_node = self.config_node['trace']
313a0bd9 1112 normalize_byte_order_prop(self._trace_type_node, 'native-byte-order')
4810b707
PP
1113
1114 for parent_node, key in self._trace_type_props():
1115 node = parent_node[key]
1116
1117 if node is None:
1118 # a `None` property is equivalent to not having it
1119 del parent_node[key]
1120 continue
1121
1122 if key == 'class' and type(node) is str:
1123 # field type class aliases
1124 if node in ['uint', 'unsigned-int']:
1125 parent_node[key] = 'unsigned-integer'
1126 elif node in ['sint', 'signed-int']:
1127 parent_node[key] = 'signed-integer'
1128 elif node in ['uenum', 'unsigned-enum']:
1129 parent_node[key] = 'unsigned-enumeration'
1130 elif node in ['senum', 'signed-enum']:
1131 parent_node[key] = 'signed-enumeration'
1132 elif node == 'str':
1133 parent_node[key] = 'string'
1134 elif node == 'struct':
1135 parent_node[key] = 'structure'
4810b707
PP
1136 elif key == 'preferred-display-base' and type(node) is str:
1137 # display base aliases
1138 if node == 'bin':
1139 parent_node[key] = 'binary'
1140 elif node == 'oct':
1141 parent_node[key] = 'octal'
1142 elif node == 'dec':
1143 parent_node[key] = 'decimal'
1144 elif node == 'hex':
1145 parent_node[key] = 'hexadecimal'
1146
1147 prop_name = 'environment'
1148
1149 if prop_name in trace_node:
1150 node = trace_node[prop_name]
1151
1152 if node is None:
1153 del trace_node[prop_name]
1154
313a0bd9
PP
1155 # Sets the parser's native byte order.
1156 def _set_native_byte_order(self):
1157 self._native_byte_order_node = self._trace_type_node['native-byte-order']
1158 self._native_byte_order = self._byte_order_from_node(self._native_byte_order_node)
4810b707 1159
e8f0d548
PP
1160 # Processes the inclusions of the event record type node
1161 # `ert_node`, returning the effective node.
1162 def _process_ert_node_include(self, ert_node: _MapNode) -> _MapNode:
1163 # Make sure the event record type node is valid for the
1164 # inclusion processing stage.
1165 self._schema_validator.validate(ert_node, 'config/3/ert-pre-include')
4810b707
PP
1166
1167 # process inclusions
e8f0d548 1168 return self._process_node_include(ert_node, self._process_ert_node_include)
4810b707 1169
e8f0d548
PP
1170 # Processes the inclusions of the data stream type node `dst_node`,
1171 # returning the effective node.
1172 def _process_dst_node_include(self, dst_node: _MapNode) -> _MapNode:
1173 def process_children_include(dst_node: _MapNode):
1174 prop_name = 'event-record-types'
4810b707 1175
e8f0d548
PP
1176 if prop_name in dst_node:
1177 erts_node = dst_node[prop_name]
4810b707 1178
e8f0d548
PP
1179 for key in list(erts_node):
1180 erts_node[key] = self._process_ert_node_include(erts_node[key])
4810b707 1181
e8f0d548 1182 # Make sure the data stream type node is valid for the inclusion
4810b707 1183 # processing stage.
e8f0d548 1184 self._schema_validator.validate(dst_node, 'config/3/dst-pre-include')
4810b707
PP
1185
1186 # process inclusions
e8f0d548 1187 return self._process_node_include(dst_node, self._process_dst_node_include,
4810b707
PP
1188 process_children_include)
1189
1190 # Processes the inclusions of the clock type node `clk_type_node`,
1191 # returning the effective node.
2d55dc7d 1192 def _process_clk_type_node_include(self, clk_type_node: _MapNode) -> _MapNode:
4810b707
PP
1193 # Make sure the clock type node is valid for the inclusion
1194 # processing stage.
c3fa1a14 1195 self._schema_validator.validate(clk_type_node, 'config/3/clock-type-pre-include')
4810b707
PP
1196
1197 # process inclusions
1198 return self._process_node_include(clk_type_node, self._process_clk_type_node_include)
1199
1200 # Processes the inclusions of the trace type node `trace_type_node`,
1201 # returning the effective node.
2d55dc7d
PP
1202 def _process_trace_type_node_include(self, trace_type_node: _MapNode) -> _MapNode:
1203 def process_children_include(trace_type_node: _MapNode):
4810b707
PP
1204 prop_name = 'clock-types'
1205
1206 if prop_name in trace_type_node:
1207 clk_types_node = trace_type_node[prop_name]
1208
1209 for key in list(clk_types_node):
1210 clk_types_node[key] = self._process_clk_type_node_include(clk_types_node[key])
1211
e8f0d548 1212 prop_name = 'data-stream-types'
4810b707
PP
1213
1214 if prop_name in trace_type_node:
e8f0d548 1215 dsts_node = trace_type_node[prop_name]
4810b707 1216
e8f0d548
PP
1217 for key in list(dsts_node):
1218 dsts_node[key] = self._process_dst_node_include(dsts_node[key])
4810b707
PP
1219
1220 # Make sure the trace type node is valid for the inclusion
1221 # processing stage.
c3fa1a14 1222 self._schema_validator.validate(trace_type_node, 'config/3/trace-type-pre-include')
4810b707
PP
1223
1224 # process inclusions
1225 return self._process_node_include(trace_type_node, self._process_trace_type_node_include,
1226 process_children_include)
1227
1228 # Processes the inclusions of the trace node `trace_node`, returning
1229 # the effective node.
2d55dc7d
PP
1230 def _process_trace_node_include(self, trace_node: _MapNode) -> _MapNode:
1231 def process_children_include(trace_node: _MapNode):
4810b707
PP
1232 prop_name = 'type'
1233 trace_node[prop_name] = self._process_trace_type_node_include(trace_node[prop_name])
1234
1235 # Make sure the trace node is valid for the inclusion processing
1236 # stage.
c3fa1a14 1237 self._schema_validator.validate(trace_node, 'config/3/trace-pre-include')
4810b707
PP
1238
1239 # process inclusions
1240 return self._process_node_include(trace_node, self._process_trace_node_include,
1241 process_children_include)
1242
1243 # Processes the inclusions of the configuration node, modifying it
1244 # during the process.
1245 def _process_config_includes(self):
1246 # Process inclusions in this order:
1247 #
e8f0d548
PP
1248 # 1. Clock type node and event record type nodes (the order
1249 # between those is not important).
4810b707 1250 #
e8f0d548 1251 # 2. Data stream type nodes.
4810b707
PP
1252 #
1253 # 3. Trace type node.
1254 #
1255 # 4. Trace node.
1256 #
1257 # This is because:
1258 #
1259 # * A trace node can include a trace type node, clock type
e8f0d548 1260 # nodes, data stream type nodes, and event record type nodes.
4810b707 1261 #
e8f0d548
PP
1262 # * A trace type node can include clock type nodes, data stream
1263 # type nodes, and event record type nodes.
4810b707 1264 #
e8f0d548 1265 # * A data stream type node can include event record type nodes.
4810b707
PP
1266 #
1267 # First, make sure the configuration node itself is valid for
1268 # the inclusion processing stage.
c3fa1a14 1269 self._schema_validator.validate(self.config_node, 'config/3/config-pre-include')
4810b707
PP
1270
1271 # Process trace node inclusions.
1272 #
1273 # self._process_trace_node_include() returns a new (or the same)
1274 # trace node without any `$include` property in it, recursively.
2d55dc7d 1275 self.config_node['trace'] = self._process_trace_node_include(self.config_node['trace'])
4810b707
PP
1276
1277 def _parse(self):
1278 # process configuration node inclusions
1279 self._process_config_includes()
1280
1281 # Expand field type nodes.
1282 #
1283 # This process:
1284 #
1285 # 1. Replaces field type aliases with "effective" field type
1286 # nodes, recursively.
1287 #
1288 # After this step, the `$field-type-aliases` property of the
1289 # trace type node is gone.
1290 #
1291 # 2. Applies inheritance, following the `$inherit` properties.
1292 #
1293 # After this step, field type nodes do not contain `$inherit`
1294 # properties.
1295 #
1296 # This is done blindly, in that the process _doesn't_ validate
1297 # field type nodes at this point.
1298 self._expand_fts()
1299
1300 # Substitute log level aliases.
1301 #
1302 # This process:
1303 #
e8f0d548
PP
1304 # 1. Replaces log level aliases in event record type nodes with
1305 # their numeric equivalents as found in the
1306 # `$log-level-aliases` property of the trace type node.
4810b707
PP
1307 #
1308 # 2. Removes the `$log-level-aliases` property from the trace
1309 # type node.
1310 self._sub_log_level_aliases()
1311
1312 # At this point, the configuration node must be valid as an
1313 # effective configuration node.
c3fa1a14 1314 self._schema_validator.validate(self.config_node, 'config/3/config')
4810b707
PP
1315
1316 # Normalize properties.
1317 #
1318 # This process removes `None` properties and chooses specific
1319 # enumerators when aliases exist (for example, `big-endian`
1320 # instead of `be`).
1321 #
1322 # The goal of this is that, if the user then gets this parser's
1323 # `config_node` property, it has a normal and very readable
1324 # form.
1325 #
1326 # It also makes _create_config() easier to implement because it
1327 # doesn't need to check for `None` nodes or enumerator aliases.
1328 self._normalize_props()
1329
313a0bd9
PP
1330 # Set the native byte order.
1331 self._set_native_byte_order()
4810b707
PP
1332
1333 # Create a barectf configuration object from the configuration
1334 # node.
1335 self._create_config()
1336
1337 @property
2d55dc7d 1338 def config(self) -> barectf_config.Configuration:
4810b707
PP
1339 return self._config
1340
1341 @property
2d55dc7d
PP
1342 def config_node(self) -> _MapNode:
1343 return typing.cast(barectf_config_parse_common._ConfigNodeV3, self._root_node).config_node
This page took 0.086818 seconds and 4 git commands to generate.