• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aas-core-works / aas-core-codegen / 17360913018

31 Aug 2025 06:35PM UTC coverage: 82.105% (+0.1%) from 82.003%
17360913018

push

github

web-flow
Upgrade pylint to 3.3.8 (#551)

We upgrade pylint to the latest version so that we can still check the
code with the newest Python 3.13.

28501 of 34713 relevant lines covered (82.1%)

3.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.8
/aas_core_codegen/cpp/constants/_generate.py
1
"""Generate C++ constants corresponding to the constants of the meta-model."""
2
import io
4✔
3
from typing import (
4✔
4
    Optional,
5
    List,
6
    Tuple,
7
)
8

9
from icontract import ensure
4✔
10

11
from aas_core_codegen import intermediate
4✔
12
from aas_core_codegen.common import (
4✔
13
    Error,
14
    assert_never,
15
    Stripped,
16
    indent_but_first_line,
17
)
18
from aas_core_codegen.cpp import (
4✔
19
    common as cpp_common,
20
    naming as cpp_naming,
21
    description as cpp_description,
22
)
23
from aas_core_codegen.cpp.common import (
4✔
24
    INDENT as I,
25
    INDENT2 as II,
26
)
27

28

29
# region Generation
30

31

32
def _generate_documentation_comment_for_constant(
4✔
33
    description: intermediate.DescriptionOfConstant,
34
    context: cpp_description.Context,
35
) -> Tuple[Optional[Stripped], Optional[List[Error]]]:
36
    """Generate the documentation comment for the given constant."""
37
    # fmt: off
38
    comment, errors = (
4✔
39
        cpp_description
40
        .generate_comment_for_summary_remarks(
41
            description=description, context=context
42
        )
43
    )
44
    # fmt: on
45

46
    if errors is not None:
4✔
47
        return None, errors
×
48

49
    assert comment is not None
4✔
50
    return comment, None
4✔
51

52

53
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
4✔
54
def _generate_constant_primitive_definition(
4✔
55
    constant: intermediate.ConstantPrimitive,
56
) -> Tuple[Optional[Stripped], Optional[Error]]:
57
    """Generate the definition of a constant primitive."""
58
    writer = io.StringIO()
×
59

60
    if constant.description is not None:
×
61
        comment, comment_errors = _generate_documentation_comment_for_constant(
×
62
            description=constant.description,
63
            context=cpp_description.Context(
64
                namespace=cpp_common.CONSTANTS_NAMESPACE, cls_or_enum=None
65
            ),
66
        )
67
        if comment_errors is not None:
×
68
            return None, Error(
×
69
                constant.parsed.node,
70
                f"Failed to generate the documentation comment for {constant.name!r}",
71
                comment_errors,
72
            )
73

74
        assert comment is not None
×
75
        writer.write(comment)
×
76
        writer.write("\n")
×
77

78
    constant_name = cpp_naming.constant_name(constant.name)
×
79

80
    if constant.a_type is intermediate.PrimitiveType.BOOL:
×
81
        writer.write(f"extern const bool {constant_name};")
×
82

83
    elif constant.a_type is intermediate.PrimitiveType.INT:
×
84
        writer.write(f"extern const int64_t {constant_name};")
×
85

86
    elif constant.a_type is intermediate.PrimitiveType.FLOAT:
×
87
        writer.write(f"extern const double {constant_name};")
×
88

89
    elif constant.a_type is intermediate.PrimitiveType.STR:
×
90
        writer.write(f"extern const std::wstring {constant_name};")
×
91

92
    elif constant.a_type is intermediate.PrimitiveType.BYTEARRAY:
×
93
        writer.write(f"extern const std::vector<std::uint8_t> {constant_name};")
×
94

95
    else:
96
        assert_never(constant.a_type)
×
97

98
    return Stripped(writer.getvalue()), None
×
99

100

101
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
4✔
102
def _generate_constant_primitive_implementation(
4✔
103
    constant: intermediate.ConstantPrimitive,
104
) -> Tuple[Optional[Stripped], Optional[Error]]:
105
    """Generate the implementation of a constant primitive."""
106
    constant_name = cpp_naming.constant_name(constant.name)
×
107

108
    if constant.a_type is intermediate.PrimitiveType.BOOL:
×
109
        assert isinstance(constant.value, bool)
×
110
        literal = cpp_common.boolean_literal(constant.value)
×
111

112
        return Stripped(f"const bool {constant_name} = {literal};"), None
×
113

114
    elif constant.a_type is intermediate.PrimitiveType.INT:
×
115
        assert isinstance(constant.value, int)
×
116

117
        if constant.value > 2**63 - 1 or constant.value < -(2**63):
×
118
            return None, Error(
×
119
                constant.parsed.node,
120
                f"The value of the constant {constant.name!r} overflows "
121
                f"64-bit signed integer",
122
            )
123
        return Stripped(f"const int64_t {constant_name} = {str(constant.value)};"), None
×
124

125
    elif constant.a_type is intermediate.PrimitiveType.FLOAT:
×
126
        assert isinstance(constant.value, float)
×
127

128
        # NOTE (mristin, 2023-07-05):
129
        # We assume that the float constants are not meant to be all to precise.
130
        # Therefore, we use a string representation here. However, beware that we
131
        # might have to use a more precise representation in the future if the spec
132
        # change.
133
        literal = cpp_common.float_literal(constant.value)
×
134

135
        return Stripped(f"const double {constant_name} = {literal};"), None
×
136

137
    elif constant.a_type is intermediate.PrimitiveType.STR:
×
138
        assert isinstance(constant.value, str)
×
139
        literal = cpp_common.wstring_literal(constant.value)
×
140

141
        return (
×
142
            Stripped(
143
                f"""\
144
const std::wstring {constant_name} = (
145
{I}{indent_but_first_line(literal, I)}
146
);"""
147
            ),
148
            None,
149
        )
150

151
    elif constant.a_type is intermediate.PrimitiveType.BYTEARRAY:
×
152
        assert isinstance(constant.value, bytearray)
×
153

154
        literal, _ = cpp_common.bytes_literal(value=constant.value)
×
155

156
        return (
×
157
            Stripped(
158
                f"""\
159
const std::vector<std::uint_8> {constant_name} = (
160
{I}{indent_but_first_line(literal, I)}
161
);"""
162
            ),
163
            None,
164
        )
165

166
    else:
167
        assert_never(constant.a_type)
×
168

169

170
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
4✔
171
def _generate_constant_set_of_primitives_definition(
4✔
172
    constant: intermediate.ConstantSetOfPrimitives,
173
) -> Tuple[Optional[Stripped], Optional[Error]]:
174
    """Generate the definition of a constant set of primitives."""
175
    errors = []  # type: List[Error]
4✔
176

177
    writer = io.StringIO()
4✔
178

179
    if constant.description is not None:
4✔
180
        comment, comment_errors = _generate_documentation_comment_for_constant(
4✔
181
            description=constant.description,
182
            context=cpp_description.Context(
183
                namespace=cpp_common.CONSTANTS_NAMESPACE, cls_or_enum=None
184
            ),
185
        )
186
        if comment_errors is not None:
4✔
187
            errors.append(
×
188
                Error(
189
                    constant.parsed.node,
190
                    f"Failed to generate the documentation comment for {constant.name!r}",
191
                    comment_errors,
192
                )
193
            )
194
        else:
195
            assert comment is not None
4✔
196
            writer.write(comment)
4✔
197
            writer.write("\n")
4✔
198

199
    set_type: Optional[str]
200

201
    if constant.a_type is intermediate.PrimitiveType.BOOL:
4✔
202
        set_type = "std::unordered_set<bool>"
×
203

204
    elif constant.a_type is intermediate.PrimitiveType.INT:
4✔
205
        set_type = "std::unordered_set<int64_t>"
×
206

207
    elif constant.a_type is intermediate.PrimitiveType.FLOAT:
4✔
208
        set_type = "std::unordered_set<double>"
×
209

210
    elif constant.a_type is intermediate.PrimitiveType.STR:
4✔
211
        set_type = "std::unordered_set<std::wstring>"
4✔
212

213
    elif constant.a_type is intermediate.PrimitiveType.BYTEARRAY:
×
214
        set_type = "std::unordered_set<std::vector<std::uint8_t>, HashBytes>"
×
215

216
    else:
217
        assert_never(constant.a_type)
×
218

219
    constant_name = cpp_naming.constant_name(constant.name)
4✔
220

221
    writer.write(
4✔
222
        f"""\
223
extern const {set_type} {constant_name};"""
224
    )
225

226
    return Stripped(writer.getvalue()), None
4✔
227

228

229
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
4✔
230
def _generate_constant_set_of_primitives_implementation(
4✔
231
    constant: intermediate.ConstantSetOfPrimitives,
232
) -> Tuple[Optional[Stripped], Optional[Error]]:
233
    """Generate the implementation of a constant set of primitives."""
234
    literal_codes = []  # type: List[str]
4✔
235

236
    set_type: str
237

238
    if constant.a_type is intermediate.PrimitiveType.BOOL:
4✔
239
        set_type = "std::unordered_set<bool>"
×
240

241
        for literal in constant.literals:
×
242
            assert isinstance(literal.value, bool)
×
243
            literal_codes.append(cpp_common.boolean_literal(literal.value))
×
244

245
    elif constant.a_type is intermediate.PrimitiveType.INT:
4✔
246
        set_type = "std::unordered_set<int64_t>"
×
247

248
        for literal in constant.literals:
×
249
            assert isinstance(literal.value, int)
×
250

251
            literal_codes.append(str(literal.value))
×
252

253
    elif constant.a_type is intermediate.PrimitiveType.FLOAT:
4✔
254
        set_type = "std::unordered_set<double>"
×
255

256
        for literal in constant.literals:
×
257
            assert isinstance(literal.value, float)
×
258

259
            literal_codes.append(cpp_common.float_literal(literal.value))
×
260

261
    elif constant.a_type is intermediate.PrimitiveType.STR:
4✔
262
        set_type = "std::unordered_set<std::wstring>"
4✔
263

264
        for literal in constant.literals:
4✔
265
            assert isinstance(literal.value, str)
4✔
266
            literal_codes.append(cpp_common.wstring_literal(literal.value))
4✔
267

268
    elif constant.a_type is intermediate.PrimitiveType.BYTEARRAY:
×
269
        set_type = Stripped(
×
270
            f"""\
271
std::unordered_set<
272
{I}std::vector<std::uint8_t>,
273
{I}HashBytes
274
>"""
275
        )
276

277
        for literal in constant.literals:
×
278
            assert isinstance(literal.value, bytearray)
×
279
            literal_code, _ = cpp_common.bytes_literal(literal.value)
×
280
            literal_codes.append(literal_code)
×
281

282
    else:
283
        assert_never(constant.a_type)
×
284

285
    literals_joined = ",\n".join(literal_codes)
4✔
286

287
    constant_name = cpp_naming.constant_name(constant.name)
4✔
288

289
    return (
4✔
290
        Stripped(
291
            f"""\
292
const {set_type} {constant_name} = {{
293
{I}{indent_but_first_line(literals_joined, I)}
294
}};"""
295
        ),
296
        None,
297
    )
298

299

300
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
4✔
301
def _generate_constant_set_of_enumeration_literals_definition(
4✔
302
    constant: intermediate.ConstantSetOfEnumerationLiterals,
303
) -> Tuple[Optional[Stripped], Optional[Error]]:
304
    """Generate the definition of a constant set of enumeration literals."""
305
    writer = io.StringIO()
4✔
306

307
    if constant.description is not None:
4✔
308
        comment, comment_errors = _generate_documentation_comment_for_constant(
4✔
309
            description=constant.description,
310
            context=cpp_description.Context(
311
                namespace=cpp_common.CONSTANTS_NAMESPACE, cls_or_enum=None
312
            ),
313
        )
314
        if comment_errors is not None:
4✔
315
            return None, Error(
×
316
                constant.parsed.node,
317
                f"Failed to generate the documentation comment for {constant.name!r}",
318
                comment_errors,
319
            )
320

321
        assert comment is not None
4✔
322
        writer.write(comment)
4✔
323
        writer.write("\n")
4✔
324

325
    enum_name = cpp_naming.enum_name(constant.enumeration.name)
4✔
326

327
    constant_name = cpp_naming.constant_name(constant.name)
4✔
328

329
    writer.write(
4✔
330
        f"extern const std::unordered_set<types::{enum_name}> {constant_name};"
331
    )
332

333
    return Stripped(writer.getvalue()), None
4✔
334

335

336
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
4✔
337
def _generate_constant_set_of_enumeration_literals_implementation(
4✔
338
    constant: intermediate.ConstantSetOfEnumerationLiterals,
339
) -> Tuple[Optional[Stripped], Optional[Error]]:
340
    """Generate the definition of a constant set of enumeration literals."""
341

342
    enum_name = cpp_naming.enum_name(constant.enumeration.name)
4✔
343

344
    literal_codes = []  # type: List[str]
4✔
345
    for literal in constant.literals:
4✔
346
        literal_name = cpp_naming.enum_literal_name(literal.name)
4✔
347

348
        literal_codes.append(f"types::{enum_name}::{literal_name}")
4✔
349

350
    constant_name = cpp_naming.constant_name(constant.name)
4✔
351

352
    literals_joined = ",\n".join(literal_codes)
4✔
353

354
    return (
4✔
355
        Stripped(
356
            f"""\
357
const std::unordered_set<types::{enum_name}> {constant_name} = {{
358
{I}{indent_but_first_line(literals_joined, I)}
359
}};"""
360
        ),
361
        None,
362
    )
363

364

365
# fmt: off
366
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
4✔
367
@ensure(
4✔
368
    lambda result:
369
    not (result[0] is not None) or result[0].endswith('\n'),
370
    "Trailing newline mandatory for valid end-of-files"
371
)
372
# fmt: on
373
def generate_header(
4✔
374
    symbol_table: intermediate.SymbolTable,
375
    library_namespace: Stripped,
376
) -> Tuple[Optional[str], Optional[List[Error]]]:
377
    """Generate C++ header code of the constants based on the symbol table."""
378
    errors = []  # type: List[Error]
4✔
379

380
    namespace = Stripped(f"{library_namespace}::{cpp_common.CONSTANTS_NAMESPACE}")
4✔
381

382
    include_guard_var = cpp_common.include_guard_var(namespace)
4✔
383

384
    include_prefix_path = cpp_common.generate_include_prefix_path(library_namespace)
4✔
385

386
    blocks = [
4✔
387
        Stripped(
388
            f"""\
389
#ifndef {include_guard_var}
390
#define {include_guard_var}"""
391
        ),
392
        cpp_common.WARNING,
393
        Stripped(
394
            f"""\
395
#include "{include_prefix_path}/types.hpp"
396

397
#pragma warning(push, 0)
398
#include <cstdint>
399
#include <unordered_set>
400
#include <vector>
401
#pragma warning(pop)"""
402
        ),
403
        cpp_common.generate_namespace_opening(library_namespace),
404
        Stripped(
405
            f"""\
406
/**
407
 * \\defgroup constants Pre-defined constants of the meta-model
408
 * @{{
409
 */
410
namespace {cpp_common.CONSTANTS_NAMESPACE} {{"""
411
        ),
412
        Stripped(
413
            f"""\
414
/**
415
 * Hash a blob of bytes based on the Java's String hash.
416
 */
417
struct HashBytes {{
418
{I}std::size_t operator()(const std::vector<std::uint8_t>& bytes) const;
419
}};"""
420
        ),
421
    ]
422

423
    for constant in symbol_table.constants:
4✔
424
        block: Optional[Stripped]
425
        error: Optional[Error]
426

427
        if isinstance(constant, intermediate.ConstantPrimitive):
4✔
428
            block, error = _generate_constant_primitive_definition(constant=constant)
×
429
        elif isinstance(constant, intermediate.ConstantSetOfPrimitives):
4✔
430
            block, error = _generate_constant_set_of_primitives_definition(
4✔
431
                constant=constant
432
            )
433
        elif isinstance(constant, intermediate.ConstantSetOfEnumerationLiterals):
4✔
434
            block, error = _generate_constant_set_of_enumeration_literals_definition(
4✔
435
                constant=constant
436
            )
437
        else:
438
            assert_never(constant)
×
439

440
        if error is not None:
4✔
441
            errors.append(error)
×
442
            continue
×
443

444
        assert block is not None
4✔
445
        blocks.append(block)
4✔
446

447
    if len(errors) > 0:
4✔
448
        return None, errors
×
449

450
    blocks.extend(
4✔
451
        [
452
            Stripped(
453
                f"""\
454
}}  // namespace {cpp_common.COMMON_NAMESPACE}
455
/**@}}*/"""
456
            ),
457
            cpp_common.generate_namespace_closing(library_namespace),
458
            cpp_common.WARNING,
459
            Stripped(f"#endif  // {include_guard_var}"),
460
        ]
461
    )
462

463
    writer = io.StringIO()
4✔
464
    for i, block in enumerate(blocks):
4✔
465
        if i > 0:
4✔
466
            writer.write("\n\n")
4✔
467

468
        writer.write(block)
4✔
469

470
    writer.write("\n")
4✔
471

472
    return writer.getvalue(), None
4✔
473

474

475
# fmt: off
476
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
4✔
477
@ensure(
4✔
478
    lambda result:
479
    not (result[0] is not None) or result[0].endswith('\n'),
480
    "Trailing newline mandatory for valid end-of-files"
481
)
482
# fmt: on
483
def generate_implementation(
4✔
484
    symbol_table: intermediate.SymbolTable,
485
    library_namespace: Stripped,
486
) -> Tuple[Optional[str], Optional[List[Error]]]:
487
    """Generate C++ implementation code of the constants based on the symbol table."""
488
    errors = []  # type: List[Error]
4✔
489

490
    namespace = Stripped(f"{library_namespace}::constants")
4✔
491

492
    include_prefix_path = cpp_common.generate_include_prefix_path(library_namespace)
4✔
493

494
    blocks = [
4✔
495
        Stripped(
496
            f'''\
497
#include "{include_prefix_path}/constants.hpp"'''
498
        ),
499
        cpp_common.WARNING,
500
        cpp_common.generate_namespace_opening(namespace),
501
        Stripped(
502
            f"""\
503
std::size_t HashBytes::operator()(
504
{I}const std::vector<std::uint8_t>& bytes
505
) const {{
506
{I}std::size_t result = 0;
507
{I}const std::size_t prime = 31;
508
{I}const std::size_t size = bytes.size();
509
{I}for (std::size_t i = 0; i < size; ++i) {{
510
{II}result = bytes[i] + (result * prime);
511
{I}}}
512
{I}return result;
513
}}"""
514
        ),
515
    ]
516

517
    for constant in symbol_table.constants:
4✔
518
        block: Optional[Stripped]
519
        error: Optional[Error]
520

521
        if isinstance(constant, intermediate.ConstantPrimitive):
4✔
522
            block, error = _generate_constant_primitive_implementation(
×
523
                constant=constant
524
            )
525
        elif isinstance(constant, intermediate.ConstantSetOfPrimitives):
4✔
526
            block, error = _generate_constant_set_of_primitives_implementation(
4✔
527
                constant=constant
528
            )
529
        elif isinstance(constant, intermediate.ConstantSetOfEnumerationLiterals):
4✔
530
            (
4✔
531
                block,
532
                error,
533
            ) = _generate_constant_set_of_enumeration_literals_implementation(
534
                constant=constant
535
            )
536
        else:
537
            assert_never(constant)
×
538

539
        if error is not None:
4✔
540
            errors.append(error)
×
541
            continue
×
542

543
        assert block is not None
4✔
544
        blocks.append(block)
4✔
545

546
    if len(errors) > 0:
4✔
547
        return None, errors
×
548

549
    blocks.extend(
4✔
550
        [
551
            cpp_common.generate_namespace_closing(namespace),
552
            cpp_common.WARNING,
553
        ]
554
    )
555

556
    writer = io.StringIO()
4✔
557
    for i, block in enumerate(blocks):
4✔
558
        if i > 0:
4✔
559
            writer.write("\n\n")
4✔
560

561
        writer.write(block)
4✔
562

563
    writer.write("\n")
4✔
564

565
    return writer.getvalue(), None
4✔
566

567

568
# endregion
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc