fprime-cpp-codegen 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/PKG-INFO +73 -1
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/README.md +72 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/__init__.py +8 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/body.py +16 -8
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/coercion.py +11 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/decoration.py +3 -2
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/definitions.py +31 -9
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/document.py +93 -8
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/scopes.py +307 -61
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/comments.py +77 -14
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/doc.py +43 -8
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/lines.py +12 -2
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/output.py +48 -16
- fprime_cpp_codegen-0.2.0/src/fprime_cpp_codegen/validation.py +256 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/writer.py +64 -16
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen.egg-info/PKG-INFO +73 -1
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen.egg-info/SOURCES.txt +3 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen.egg-info/scm_file_list.json +3 -0
- fprime_cpp_codegen-0.2.0/src/fprime_cpp_codegen.egg-info/scm_version.json +8 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_body.py +29 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_builder.py +162 -4
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_comments.py +79 -1
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_lines.py +27 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_output.py +98 -2
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_readme.py +59 -0
- fprime_cpp_codegen-0.2.0/tests/test_signatures.py +130 -0
- fprime_cpp_codegen-0.2.0/tests/test_validation.py +414 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_writer_constructs.py +179 -2
- fprime_cpp_codegen-0.1.0/src/fprime_cpp_codegen.egg-info/scm_version.json +0 -8
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/.github/workflows/ci.yml +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/.github/workflows/format.yml +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/.github/workflows/publish.yml +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/.gitignore +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/LICENSE +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/examples/fpp_constants.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/examples/fpp_enum.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/examples/ring_buffer.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/pyproject.toml +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/setup.cfg +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/__init__.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/base.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/errors.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/formatting.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/fprime.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/py.typed +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen.egg-info/dependency_links.txt +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen.egg-info/requires.txt +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen.egg-info/top_level.txt +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/__init__.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/conftest.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/goldens/C.cpp +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/goldens/C.hpp +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/goldens/Other.cpp +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/goldens/fpp/EEnumAc.ref.cpp +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/goldens/fpp/EEnumAc.ref.hpp +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/goldens/fpp/FppConstantsAc.ref.cpp +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/goldens/fpp/FppConstantsAc.ref.hpp +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_examples.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_formatting.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_fpp_examples.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_fprime.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_golden_builder.py +0 -0
- {fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/tests/test_golden_ir.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fprime-cpp-codegen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A Python package for generating C++ code for F Prime
|
|
5
5
|
Author: Andrei Tumbar
|
|
6
6
|
License: Apache License
|
|
@@ -310,6 +310,78 @@ create it, so you can keep filling it in afterwards. It is worth using on access
|
|
|
310
310
|
sections and preprocessor guards, which delete themselves when nothing lands
|
|
311
311
|
inside them.
|
|
312
312
|
|
|
313
|
+
## Text that comes from your model
|
|
314
|
+
|
|
315
|
+
Multi-line strings are margin-stripped: on each line, leading whitespace followed by
|
|
316
|
+
a `|` is dropped, so a block of C++ can be indented to match the Python around it.
|
|
317
|
+
That is convenient for literals written by hand and a hazard for text a generator
|
|
318
|
+
derives from its input — an FPP annotation used as a doc comment, a C++ expression
|
|
319
|
+
whose continuation line starts with `|`, would silently lose that character.
|
|
320
|
+
|
|
321
|
+
Three escapes, in order of preference:
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
doc.lines(expression, margin=None) # no stripping at all
|
|
325
|
+
body.line(expression) # one line, never stripped
|
|
326
|
+
cls.function("f", comment=lines(text, margin=None)) # comments take Lines, too
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Every `comment=` argument accepts either a string, which is stripped, or a
|
|
330
|
+
`Sequence[Line]`, which is not. `Body.line()` and `Body.raw()` never strip;
|
|
331
|
+
`Body.lines()` and the scope-level `lines()` take `margin=None`. Doubling the marker
|
|
332
|
+
(`||x`) also works, since only the first one per line is removed.
|
|
333
|
+
|
|
334
|
+
Separately, a line whose text begins with a recognised preprocessor directive —
|
|
335
|
+
`#include`, `#if`, `#pragma` and the rest — is forced to column zero, wherever in the
|
|
336
|
+
document it sits. Content that merely starts with `#`, such as a Markdown heading
|
|
337
|
+
inside a C++ string literal, keeps its indentation. A real directive that has to keep
|
|
338
|
+
its column carries the spaces in the text (`line(" #include <x>")`) rather than in
|
|
339
|
+
the line's indent.
|
|
340
|
+
|
|
341
|
+
## Extension points
|
|
342
|
+
|
|
343
|
+
Declarations take attributes, which is what a shared object's exported symbols need.
|
|
344
|
+
On a class they land between the keyword and the name, leaving the name — and so the
|
|
345
|
+
constructor, the destructor and every `MyComp ::` qualifier — untouched:
|
|
346
|
+
|
|
347
|
+
```python
|
|
348
|
+
cls = ns.class_("MyComp", extends="public MyCompComponentBase",
|
|
349
|
+
attributes='__attribute__((visibility("default")))')
|
|
350
|
+
cls.function("get", ret="U32", attributes="[[nodiscard]]", body="return m_v;")
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
A document does not have to be two files. `emit_hpp=False` gives a standalone
|
|
354
|
+
translation unit — a `PYBIND11_MODULE` block with no header to declare — and
|
|
355
|
+
`emit_cpp=False` a header-only one. Either way, anything the dropped file was the
|
|
356
|
+
only home for raises a `ValidationError` naming it, rather than disappearing:
|
|
357
|
+
|
|
358
|
+
```python
|
|
359
|
+
doc = CppDocBuilder("MyCompModule", emit_hpp=False, strict=True)
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
`strict=True` additionally rejects a definition that needs a body and has none, which
|
|
363
|
+
otherwise renders as an empty out-of-line definition — valid C++, and so easy for a
|
|
364
|
+
generator to emit by accident. Say `declaration_only=True` for a header-only
|
|
365
|
+
declaration, or `body=""` for a definition that is deliberately empty.
|
|
366
|
+
`fprime_cpp_codegen.validation` reports both classes of problem without raising.
|
|
367
|
+
|
|
368
|
+
To change how something renders, subclass `HppWriter` or `CppWriter` and pass the
|
|
369
|
+
instance in. Every output path takes one, so overriding a single method costs you
|
|
370
|
+
nothing else — `write()` keeps its directory creation, its unchanged-file mtime
|
|
371
|
+
handling and its formatter:
|
|
372
|
+
|
|
373
|
+
```python
|
|
374
|
+
class GuardlessHpp(HppWriter):
|
|
375
|
+
def open_include_guard(self, guard: str) -> list[Line]:
|
|
376
|
+
return lines("#pragma once")
|
|
377
|
+
|
|
378
|
+
def close_include_guard(self) -> list[Line]:
|
|
379
|
+
return []
|
|
380
|
+
|
|
381
|
+
doc = CppDocBuilder("MyComp", hpp_writer=GuardlessHpp())
|
|
382
|
+
doc.write("build-artifacts") # or pass writer= / hpp_writer= per call
|
|
383
|
+
```
|
|
384
|
+
|
|
313
385
|
## Examples
|
|
314
386
|
|
|
315
387
|
Run any of these to print the C++ they generate, or pass a directory to write it:
|
|
@@ -77,6 +77,78 @@ create it, so you can keep filling it in afterwards. It is worth using on access
|
|
|
77
77
|
sections and preprocessor guards, which delete themselves when nothing lands
|
|
78
78
|
inside them.
|
|
79
79
|
|
|
80
|
+
## Text that comes from your model
|
|
81
|
+
|
|
82
|
+
Multi-line strings are margin-stripped: on each line, leading whitespace followed by
|
|
83
|
+
a `|` is dropped, so a block of C++ can be indented to match the Python around it.
|
|
84
|
+
That is convenient for literals written by hand and a hazard for text a generator
|
|
85
|
+
derives from its input — an FPP annotation used as a doc comment, a C++ expression
|
|
86
|
+
whose continuation line starts with `|`, would silently lose that character.
|
|
87
|
+
|
|
88
|
+
Three escapes, in order of preference:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
doc.lines(expression, margin=None) # no stripping at all
|
|
92
|
+
body.line(expression) # one line, never stripped
|
|
93
|
+
cls.function("f", comment=lines(text, margin=None)) # comments take Lines, too
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Every `comment=` argument accepts either a string, which is stripped, or a
|
|
97
|
+
`Sequence[Line]`, which is not. `Body.line()` and `Body.raw()` never strip;
|
|
98
|
+
`Body.lines()` and the scope-level `lines()` take `margin=None`. Doubling the marker
|
|
99
|
+
(`||x`) also works, since only the first one per line is removed.
|
|
100
|
+
|
|
101
|
+
Separately, a line whose text begins with a recognised preprocessor directive —
|
|
102
|
+
`#include`, `#if`, `#pragma` and the rest — is forced to column zero, wherever in the
|
|
103
|
+
document it sits. Content that merely starts with `#`, such as a Markdown heading
|
|
104
|
+
inside a C++ string literal, keeps its indentation. A real directive that has to keep
|
|
105
|
+
its column carries the spaces in the text (`line(" #include <x>")`) rather than in
|
|
106
|
+
the line's indent.
|
|
107
|
+
|
|
108
|
+
## Extension points
|
|
109
|
+
|
|
110
|
+
Declarations take attributes, which is what a shared object's exported symbols need.
|
|
111
|
+
On a class they land between the keyword and the name, leaving the name — and so the
|
|
112
|
+
constructor, the destructor and every `MyComp ::` qualifier — untouched:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
cls = ns.class_("MyComp", extends="public MyCompComponentBase",
|
|
116
|
+
attributes='__attribute__((visibility("default")))')
|
|
117
|
+
cls.function("get", ret="U32", attributes="[[nodiscard]]", body="return m_v;")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
A document does not have to be two files. `emit_hpp=False` gives a standalone
|
|
121
|
+
translation unit — a `PYBIND11_MODULE` block with no header to declare — and
|
|
122
|
+
`emit_cpp=False` a header-only one. Either way, anything the dropped file was the
|
|
123
|
+
only home for raises a `ValidationError` naming it, rather than disappearing:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
doc = CppDocBuilder("MyCompModule", emit_hpp=False, strict=True)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`strict=True` additionally rejects a definition that needs a body and has none, which
|
|
130
|
+
otherwise renders as an empty out-of-line definition — valid C++, and so easy for a
|
|
131
|
+
generator to emit by accident. Say `declaration_only=True` for a header-only
|
|
132
|
+
declaration, or `body=""` for a definition that is deliberately empty.
|
|
133
|
+
`fprime_cpp_codegen.validation` reports both classes of problem without raising.
|
|
134
|
+
|
|
135
|
+
To change how something renders, subclass `HppWriter` or `CppWriter` and pass the
|
|
136
|
+
instance in. Every output path takes one, so overriding a single method costs you
|
|
137
|
+
nothing else — `write()` keeps its directory creation, its unchanged-file mtime
|
|
138
|
+
handling and its formatter:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
class GuardlessHpp(HppWriter):
|
|
142
|
+
def open_include_guard(self, guard: str) -> list[Line]:
|
|
143
|
+
return lines("#pragma once")
|
|
144
|
+
|
|
145
|
+
def close_include_guard(self) -> list[Line]:
|
|
146
|
+
return []
|
|
147
|
+
|
|
148
|
+
doc = CppDocBuilder("MyComp", hpp_writer=GuardlessHpp())
|
|
149
|
+
doc.write("build-artifacts") # or pass writer= / hpp_writer= per call
|
|
150
|
+
```
|
|
151
|
+
|
|
80
152
|
## Examples
|
|
81
153
|
|
|
82
154
|
Run any of these to print the C++ they generate, or pass a directory to write it:
|
|
@@ -10,6 +10,7 @@ The package is layered, and you can enter at whichever level suits the job:
|
|
|
10
10
|
* :mod:`~fprime_cpp_codegen.comments` -- comment and banner formatting.
|
|
11
11
|
* :mod:`~fprime_cpp_codegen.lines` -- the line model everything is built on.
|
|
12
12
|
* :mod:`~fprime_cpp_codegen.output` -- rendering to text and to disk.
|
|
13
|
+
* :mod:`~fprime_cpp_codegen.validation` -- whole-document checks.
|
|
13
14
|
* :mod:`~fprime_cpp_codegen.formatting` -- optional post-processing through
|
|
14
15
|
``clang-format``.
|
|
15
16
|
* :mod:`~fprime_cpp_codegen.fprime` -- F Prime conventions. Import it explicitly;
|
|
@@ -52,6 +53,7 @@ from .doc import (
|
|
|
52
53
|
VOID,
|
|
53
54
|
Class,
|
|
54
55
|
ClassMember,
|
|
56
|
+
Comment,
|
|
55
57
|
Constructor,
|
|
56
58
|
CppDoc,
|
|
57
59
|
DefaultFileBanner,
|
|
@@ -83,6 +85,7 @@ from .lines import (
|
|
|
83
85
|
wrap_in_scope,
|
|
84
86
|
)
|
|
85
87
|
from .output import WriteResult, collect_cpp_files, doc_files, write_doc
|
|
88
|
+
from .validation import check_document, orphaned_members, unfilled_definitions
|
|
86
89
|
from .writer import (
|
|
87
90
|
Context,
|
|
88
91
|
CppWriter,
|
|
@@ -108,6 +111,7 @@ __all__ = [
|
|
|
108
111
|
"VOID",
|
|
109
112
|
"Class",
|
|
110
113
|
"ClassMember",
|
|
114
|
+
"Comment",
|
|
111
115
|
"Constructor",
|
|
112
116
|
"CppDoc",
|
|
113
117
|
"DefaultFileBanner",
|
|
@@ -155,6 +159,10 @@ __all__ = [
|
|
|
155
159
|
"collect_cpp_files",
|
|
156
160
|
"doc_files",
|
|
157
161
|
"write_doc",
|
|
162
|
+
# Validation
|
|
163
|
+
"check_document",
|
|
164
|
+
"orphaned_members",
|
|
165
|
+
"unfilled_definitions",
|
|
158
166
|
# Errors
|
|
159
167
|
"CppCodegenError",
|
|
160
168
|
"ScopeError",
|
|
@@ -34,6 +34,7 @@ from .comments import (
|
|
|
34
34
|
write_comment_body,
|
|
35
35
|
write_doxygen_comment,
|
|
36
36
|
)
|
|
37
|
+
from .doc import Comment
|
|
37
38
|
from .errors import ScopeError, ValidationError
|
|
38
39
|
from .lines import Line, blank, indent_lines
|
|
39
40
|
from .lines import line as _line
|
|
@@ -44,7 +45,9 @@ __all__ = ["Body", "Code", "Switch", "stmts"]
|
|
|
44
45
|
|
|
45
46
|
#: Anything usable as a run of C++ statements. ``None`` contributes nothing, so
|
|
46
47
|
#: ``b.add(frag if condition else None)`` needs no branch. A ``str`` is
|
|
47
|
-
#: margin-stripped and taken verbatim, with no punctuation added
|
|
48
|
+
#: margin-stripped and taken verbatim, with no punctuation added; pass a
|
|
49
|
+
#: :class:`~fprime_cpp_codegen.lines.Line` instead, or use :meth:`Body.line`, for text
|
|
50
|
+
#: that must survive a leading ``|``.
|
|
48
51
|
Code: TypeAlias = "None | str | Line | Body | Sequence[Code]"
|
|
49
52
|
|
|
50
53
|
|
|
@@ -216,9 +219,14 @@ class Body:
|
|
|
216
219
|
"""Append one line verbatim."""
|
|
217
220
|
return self._emit([_line(text)])
|
|
218
221
|
|
|
219
|
-
def lines(self, text: str) -> Body:
|
|
220
|
-
"""Append a margin-stripped, possibly multi-line block of C++.
|
|
221
|
-
|
|
222
|
+
def lines(self, text: str, *, margin: str | None = "|") -> Body:
|
|
223
|
+
"""Append a margin-stripped, possibly multi-line block of C++.
|
|
224
|
+
|
|
225
|
+
``margin=None`` turns the stripping off, for text taken from a generator's
|
|
226
|
+
input that may legitimately begin with the margin character. :meth:`line`
|
|
227
|
+
never strips.
|
|
228
|
+
"""
|
|
229
|
+
return self._emit(_lines(text, margin=margin))
|
|
222
230
|
|
|
223
231
|
def raw(self, ll: Iterable[Line]) -> Body:
|
|
224
232
|
"""Append already-rendered lines."""
|
|
@@ -241,19 +249,19 @@ class Body:
|
|
|
241
249
|
# Comments
|
|
242
250
|
# ------------------------------------------------------------------
|
|
243
251
|
|
|
244
|
-
def comment(self, text:
|
|
252
|
+
def comment(self, text: Comment) -> Body:
|
|
245
253
|
"""Append a ``//`` comment with no leading blank line."""
|
|
246
254
|
return self._emit(write_comment_body(text))
|
|
247
255
|
|
|
248
|
-
def spaced_comment(self, text:
|
|
256
|
+
def spaced_comment(self, text: Comment) -> Body:
|
|
249
257
|
"""Append a ``//`` comment preceded by a blank line."""
|
|
250
258
|
return self._emit(write_comment(text))
|
|
251
259
|
|
|
252
|
-
def doc_comment(self, text:
|
|
260
|
+
def doc_comment(self, text: Comment) -> Body:
|
|
253
261
|
"""Append a ``//!`` doxygen comment."""
|
|
254
262
|
return self._emit(write_doxygen_comment(text))
|
|
255
263
|
|
|
256
|
-
def banner(self, text:
|
|
264
|
+
def banner(self, text: Comment) -> Body:
|
|
257
265
|
"""Append a ruled banner comment."""
|
|
258
266
|
return self._emit(write_banner_comment(text))
|
|
259
267
|
|
{fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/coercion.py
RENAMED
|
@@ -95,3 +95,14 @@ def _extends(extends: str | Sequence[str] | None) -> str | None:
|
|
|
95
95
|
return extends
|
|
96
96
|
joined = ", ".join(extends)
|
|
97
97
|
return joined or None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _as_attributes(attributes: str | Sequence[str]) -> tuple[str, ...]:
|
|
101
|
+
"""Normalise a declaration-attribute specification into a tuple.
|
|
102
|
+
|
|
103
|
+
A single string is one attribute, not a sequence of characters, since one is the
|
|
104
|
+
common case: ``attributes='__attribute__((visibility("default")))'``.
|
|
105
|
+
"""
|
|
106
|
+
if isinstance(attributes, str):
|
|
107
|
+
return (attributes,)
|
|
108
|
+
return tuple(attributes)
|
{fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/decoration.py
RENAMED
|
@@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
12
12
|
from ..comments import write_access_tag, write_banner_comment
|
|
13
13
|
from ..doc import (
|
|
14
14
|
Class,
|
|
15
|
+
Comment,
|
|
15
16
|
Constructor,
|
|
16
17
|
Destructor,
|
|
17
18
|
Function,
|
|
@@ -90,7 +91,7 @@ class _SectionBanner(_Builder[Lines]):
|
|
|
90
91
|
definitions went.
|
|
91
92
|
"""
|
|
92
93
|
|
|
93
|
-
def __init__(self, comment:
|
|
94
|
+
def __init__(self, comment: Comment) -> None:
|
|
94
95
|
self.comment = comment
|
|
95
96
|
self.members: list[object] = []
|
|
96
97
|
|
|
@@ -179,7 +180,7 @@ class AccessSection:
|
|
|
179
180
|
its label and banner back out again.
|
|
180
181
|
"""
|
|
181
182
|
|
|
182
|
-
def __init__(self, scope: ClassBuilder, tag: str, comment:
|
|
183
|
+
def __init__(self, scope: ClassBuilder, tag: str, comment: Comment | None) -> None:
|
|
183
184
|
self._scope = scope
|
|
184
185
|
self._count = 1
|
|
185
186
|
scope._add(Lines(write_access_tag(tag), Output.HPP))
|
{fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/definitions.py
RENAMED
|
@@ -11,12 +11,22 @@ from ..comments import (
|
|
|
11
11
|
write_doxygen_comment,
|
|
12
12
|
write_doxygen_comment_opt,
|
|
13
13
|
)
|
|
14
|
-
from ..doc import
|
|
14
|
+
from ..doc import (
|
|
15
|
+
Comment,
|
|
16
|
+
Constructor,
|
|
17
|
+
Destructor,
|
|
18
|
+
Function,
|
|
19
|
+
Lines,
|
|
20
|
+
Output,
|
|
21
|
+
Param,
|
|
22
|
+
Type,
|
|
23
|
+
as_type,
|
|
24
|
+
)
|
|
15
25
|
from ..errors import ValidationError
|
|
16
26
|
from ..lines import Line, wrap_in_scope
|
|
17
27
|
from ..lines import line as _line
|
|
18
28
|
from .base import _Builder
|
|
19
|
-
from .coercion import _as_body_lines, _as_params, _sv_qualifier
|
|
29
|
+
from .coercion import _as_attributes, _as_body_lines, _as_params, _sv_qualifier
|
|
20
30
|
|
|
21
31
|
|
|
22
32
|
class Radix(Enum):
|
|
@@ -53,7 +63,7 @@ class FunctionBuilder(_Builder[Function]):
|
|
|
53
63
|
*,
|
|
54
64
|
ret: Type | str = "void",
|
|
55
65
|
params: Iterable[Param | Sequence[str]] = (),
|
|
56
|
-
comment:
|
|
66
|
+
comment: Comment | None = None,
|
|
57
67
|
body: Code = None,
|
|
58
68
|
const: bool = False,
|
|
59
69
|
static: bool = False,
|
|
@@ -66,6 +76,8 @@ class FunctionBuilder(_Builder[Function]):
|
|
|
66
76
|
noexcept: bool = False,
|
|
67
77
|
deleted: bool = False,
|
|
68
78
|
defaulted: bool = False,
|
|
79
|
+
declaration_only: bool = False,
|
|
80
|
+
attributes: str | Sequence[str] = (),
|
|
69
81
|
template: str | None = None,
|
|
70
82
|
inline_body: bool = False,
|
|
71
83
|
cpp_file: str | None = None,
|
|
@@ -81,6 +93,8 @@ class FunctionBuilder(_Builder[Function]):
|
|
|
81
93
|
self.noexcept = noexcept
|
|
82
94
|
self.deleted = deleted
|
|
83
95
|
self.defaulted = defaulted
|
|
96
|
+
self.declaration_only = declaration_only
|
|
97
|
+
self.attributes = _as_attributes(attributes)
|
|
84
98
|
self.template = template
|
|
85
99
|
self.inline_body = inline_body
|
|
86
100
|
self.cpp_file = cpp_file
|
|
@@ -99,7 +113,7 @@ class FunctionBuilder(_Builder[Function]):
|
|
|
99
113
|
type_name: Type | str,
|
|
100
114
|
name: str,
|
|
101
115
|
*,
|
|
102
|
-
comment:
|
|
116
|
+
comment: Comment | None = None,
|
|
103
117
|
default: str | None = None,
|
|
104
118
|
) -> FunctionBuilder:
|
|
105
119
|
"""Append one formal parameter. Returns self, so calls can be chained."""
|
|
@@ -125,6 +139,8 @@ class FunctionBuilder(_Builder[Function]):
|
|
|
125
139
|
noexcept=self.noexcept,
|
|
126
140
|
deleted=self.deleted,
|
|
127
141
|
defaulted=self.defaulted,
|
|
142
|
+
declaration_only=self.declaration_only,
|
|
143
|
+
attributes=self.attributes,
|
|
128
144
|
template=self.template,
|
|
129
145
|
inline_body=self.inline_body,
|
|
130
146
|
cpp_file=self.cpp_file,
|
|
@@ -145,13 +161,14 @@ class ConstructorBuilder(_Builder[Constructor]):
|
|
|
145
161
|
*,
|
|
146
162
|
params: Iterable[Param | Sequence[str]] = (),
|
|
147
163
|
initializers: Iterable[str] = (),
|
|
148
|
-
comment:
|
|
164
|
+
comment: Comment | None = None,
|
|
149
165
|
body: Code = None,
|
|
150
166
|
explicit: bool = False,
|
|
151
167
|
constexpr: bool = False,
|
|
152
168
|
noexcept: bool = False,
|
|
153
169
|
deleted: bool = False,
|
|
154
170
|
defaulted: bool = False,
|
|
171
|
+
declaration_only: bool = False,
|
|
155
172
|
template: str | None = None,
|
|
156
173
|
inline_body: bool = False,
|
|
157
174
|
cpp_file: str | None = None,
|
|
@@ -162,6 +179,7 @@ class ConstructorBuilder(_Builder[Constructor]):
|
|
|
162
179
|
self.noexcept = noexcept
|
|
163
180
|
self.deleted = deleted
|
|
164
181
|
self.defaulted = defaulted
|
|
182
|
+
self.declaration_only = declaration_only
|
|
165
183
|
self.template = template
|
|
166
184
|
self.inline_body = inline_body
|
|
167
185
|
self.cpp_file = cpp_file
|
|
@@ -174,7 +192,7 @@ class ConstructorBuilder(_Builder[Constructor]):
|
|
|
174
192
|
type_name: Type | str,
|
|
175
193
|
name: str,
|
|
176
194
|
*,
|
|
177
|
-
comment:
|
|
195
|
+
comment: Comment | None = None,
|
|
178
196
|
default: str | None = None,
|
|
179
197
|
) -> ConstructorBuilder:
|
|
180
198
|
"""Append one formal parameter."""
|
|
@@ -202,6 +220,7 @@ class ConstructorBuilder(_Builder[Constructor]):
|
|
|
202
220
|
noexcept=self.noexcept,
|
|
203
221
|
deleted=self.deleted,
|
|
204
222
|
defaulted=self.defaulted,
|
|
223
|
+
declaration_only=self.declaration_only,
|
|
205
224
|
template=self.template,
|
|
206
225
|
inline_body=self.inline_body,
|
|
207
226
|
cpp_file=self.cpp_file,
|
|
@@ -220,13 +239,14 @@ class DestructorBuilder(_Builder[Destructor]):
|
|
|
220
239
|
def __init__(
|
|
221
240
|
self,
|
|
222
241
|
*,
|
|
223
|
-
comment:
|
|
242
|
+
comment: Comment | None = None,
|
|
224
243
|
body: Code = None,
|
|
225
244
|
virtual: bool = False,
|
|
226
245
|
override: bool = False,
|
|
227
246
|
noexcept: bool = False,
|
|
228
247
|
deleted: bool = False,
|
|
229
248
|
defaulted: bool = False,
|
|
249
|
+
declaration_only: bool = False,
|
|
230
250
|
inline_body: bool = False,
|
|
231
251
|
cpp_file: str | None = None,
|
|
232
252
|
) -> None:
|
|
@@ -236,6 +256,7 @@ class DestructorBuilder(_Builder[Destructor]):
|
|
|
236
256
|
self.noexcept = noexcept
|
|
237
257
|
self.deleted = deleted
|
|
238
258
|
self.defaulted = defaulted
|
|
259
|
+
self.declaration_only = declaration_only
|
|
239
260
|
self.inline_body = inline_body
|
|
240
261
|
self.cpp_file = cpp_file
|
|
241
262
|
self.body = Body(_as_body_lines(body))
|
|
@@ -249,6 +270,7 @@ class DestructorBuilder(_Builder[Destructor]):
|
|
|
249
270
|
noexcept=self.noexcept,
|
|
250
271
|
deleted=self.deleted,
|
|
251
272
|
defaulted=self.defaulted,
|
|
273
|
+
declaration_only=self.declaration_only,
|
|
252
274
|
inline_body=self.inline_body,
|
|
253
275
|
cpp_file=self.cpp_file,
|
|
254
276
|
)
|
|
@@ -269,7 +291,7 @@ class EnumBuilder(_Builder[Lines]):
|
|
|
269
291
|
*,
|
|
270
292
|
underlying: str | None = None,
|
|
271
293
|
scoped: bool = False,
|
|
272
|
-
comment:
|
|
294
|
+
comment: Comment | None = None,
|
|
273
295
|
output: Output = Output.HPP,
|
|
274
296
|
cpp_file: str | None = None,
|
|
275
297
|
radix: Radix = Radix.DECIMAL,
|
|
@@ -323,7 +345,7 @@ class EnumBuilder(_Builder[Lines]):
|
|
|
323
345
|
name: str,
|
|
324
346
|
value: int | str | None = None,
|
|
325
347
|
*,
|
|
326
|
-
comment:
|
|
348
|
+
comment: Comment | None = None,
|
|
327
349
|
radix: Radix | None = None,
|
|
328
350
|
) -> EnumBuilder:
|
|
329
351
|
"""Append one enumerator.
|
{fprime_cpp_codegen-0.1.0 → fprime_cpp_codegen-0.2.0}/src/fprime_cpp_codegen/builder/document.py
RENAMED
|
@@ -10,7 +10,8 @@ from ..doc import CppDoc, FileBanner, HppFile
|
|
|
10
10
|
from ..errors import ValidationError
|
|
11
11
|
from ..formatting import Formatter
|
|
12
12
|
from ..output import WriteResult, doc_files, write_doc
|
|
13
|
-
from ..
|
|
13
|
+
from ..validation import check_document
|
|
14
|
+
from ..writer import CppWriter, HppWriter, render_cpp, render_hpp
|
|
14
15
|
from .base import _DocContext
|
|
15
16
|
from .scopes import _MemberScope
|
|
16
17
|
|
|
@@ -28,8 +29,13 @@ class CppDocBuilder(_MemberScope[CppDoc]):
|
|
|
28
29
|
tool_name: str | None = None,
|
|
29
30
|
file_banner: FileBanner | None = None,
|
|
30
31
|
formatter: Formatter | None = None,
|
|
32
|
+
hpp_writer: HppWriter | None = None,
|
|
33
|
+
cpp_writer: CppWriter | None = None,
|
|
31
34
|
hpp_extension: str = "hpp",
|
|
32
35
|
cpp_extension: str = "cpp",
|
|
36
|
+
emit_hpp: bool = True,
|
|
37
|
+
emit_cpp: bool = True,
|
|
38
|
+
strict: bool = False,
|
|
33
39
|
) -> None:
|
|
34
40
|
"""Start a document whose files are named after ``file_base``.
|
|
35
41
|
|
|
@@ -39,7 +45,20 @@ class CppDocBuilder(_MemberScope[CppDoc]):
|
|
|
39
45
|
|
|
40
46
|
``formatter`` post-processes every file this document renders; see
|
|
41
47
|
:mod:`fprime_cpp_codegen.formatting`. Any render or write call can override
|
|
42
|
-
it, but cannot switch it off.
|
|
48
|
+
it, but cannot switch it off. ``hpp_writer`` and ``cpp_writer`` work the same
|
|
49
|
+
way, substituting a :class:`~fprime_cpp_codegen.writer.DocWriter` subclass for
|
|
50
|
+
the default rendering.
|
|
51
|
+
|
|
52
|
+
``emit_hpp=False`` or ``emit_cpp=False`` makes this a one-file document, which
|
|
53
|
+
a translation unit holding only a module-initialisation block wants. Members
|
|
54
|
+
that would then have nowhere to go raise :class:`ValidationError` when the
|
|
55
|
+
document is built, rather than disappearing.
|
|
56
|
+
|
|
57
|
+
``strict=True`` additionally rejects any definition that needs a body and has
|
|
58
|
+
none, which would otherwise render as an empty out-of-line definition -- valid
|
|
59
|
+
C++, and so easy for a generator to emit by accident. Say
|
|
60
|
+
``declaration_only=True`` to declare without defining, or ``body=""`` for a
|
|
61
|
+
definition that is deliberately empty.
|
|
43
62
|
"""
|
|
44
63
|
super().__init__(_DocContext())
|
|
45
64
|
if not file_base:
|
|
@@ -52,6 +71,23 @@ class CppDocBuilder(_MemberScope[CppDoc]):
|
|
|
52
71
|
self.formatter = formatter
|
|
53
72
|
"""Applied to every file this document renders, unless a call overrides it."""
|
|
54
73
|
|
|
74
|
+
self.hpp_writer = hpp_writer
|
|
75
|
+
"""Renders the header, unless a call overrides it. ``None`` uses
|
|
76
|
+
:class:`~fprime_cpp_codegen.writer.HppWriter`."""
|
|
77
|
+
|
|
78
|
+
self.cpp_writer = cpp_writer
|
|
79
|
+
"""Renders the source files, unless a call overrides it. ``None`` uses
|
|
80
|
+
:class:`~fprime_cpp_codegen.writer.CppWriter`."""
|
|
81
|
+
|
|
82
|
+
self.emit_hpp = emit_hpp
|
|
83
|
+
"""Whether this document produces a header at all."""
|
|
84
|
+
|
|
85
|
+
self.emit_cpp = emit_cpp
|
|
86
|
+
"""Whether this document produces any source file at all."""
|
|
87
|
+
|
|
88
|
+
self.strict = strict
|
|
89
|
+
"""Whether :meth:`build` rejects a definition that needs a body and has none."""
|
|
90
|
+
|
|
55
91
|
self.file_banner = file_banner
|
|
56
92
|
"""Overrides the ``\\title``/``\\author``/``\\brief`` block atop each file.
|
|
57
93
|
Distinct from :meth:`banner`, which emits a section comment."""
|
|
@@ -73,7 +109,8 @@ class CppDocBuilder(_MemberScope[CppDoc]):
|
|
|
73
109
|
return f"{self.file_base}.{self.cpp_extension}"
|
|
74
110
|
|
|
75
111
|
def build(self) -> CppDoc:
|
|
76
|
-
|
|
112
|
+
"""Produce the IR, checking it against ``emit_hpp``/``emit_cpp``/``strict``."""
|
|
113
|
+
doc = CppDoc(
|
|
77
114
|
description=self.description,
|
|
78
115
|
hpp_file=HppFile(self.hpp_name, self.include_guard),
|
|
79
116
|
cpp_file_name=self.cpp_name,
|
|
@@ -81,23 +118,55 @@ class CppDocBuilder(_MemberScope[CppDoc]):
|
|
|
81
118
|
tool_name=self.tool_name,
|
|
82
119
|
banner=self.file_banner,
|
|
83
120
|
)
|
|
121
|
+
check_document(
|
|
122
|
+
doc,
|
|
123
|
+
emit_hpp=self.emit_hpp,
|
|
124
|
+
emit_cpp=self.emit_cpp,
|
|
125
|
+
strict=self.strict,
|
|
126
|
+
)
|
|
127
|
+
return doc
|
|
84
128
|
|
|
85
129
|
# -- output -------------------------------------------------------
|
|
86
130
|
|
|
87
131
|
def _formatter(self, override: Formatter | None) -> Formatter | None:
|
|
88
132
|
return override if override is not None else self.formatter
|
|
89
133
|
|
|
90
|
-
def
|
|
134
|
+
def _hpp_writer(self, override: HppWriter | None) -> HppWriter | None:
|
|
135
|
+
return override if override is not None else self.hpp_writer
|
|
136
|
+
|
|
137
|
+
def _cpp_writer(self, override: CppWriter | None) -> CppWriter | None:
|
|
138
|
+
return override if override is not None else self.cpp_writer
|
|
139
|
+
|
|
140
|
+
def _require_emitted(self, which: str) -> None:
|
|
141
|
+
"""Reject rendering a file this document was told not to produce."""
|
|
142
|
+
if not (self.emit_hpp if which == "hpp" else self.emit_cpp):
|
|
143
|
+
raise ValidationError(
|
|
144
|
+
f"this document was built with emit_{which}=False, so it has no "
|
|
145
|
+
f"{which} file to render"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
def render_hpp(
|
|
149
|
+
self,
|
|
150
|
+
*,
|
|
151
|
+
formatter: Formatter | None = None,
|
|
152
|
+
writer: HppWriter | None = None,
|
|
153
|
+
) -> str:
|
|
91
154
|
"""Render the header as text."""
|
|
92
|
-
|
|
155
|
+
self._require_emitted("hpp")
|
|
156
|
+
text = render_hpp(self.build(), writer=self._hpp_writer(writer))
|
|
93
157
|
chosen = self._formatter(formatter)
|
|
94
158
|
return chosen(text, self.hpp_name) if chosen else text
|
|
95
159
|
|
|
96
160
|
def render_cpp(
|
|
97
|
-
self,
|
|
161
|
+
self,
|
|
162
|
+
cpp_file: str | None = None,
|
|
163
|
+
*,
|
|
164
|
+
formatter: Formatter | None = None,
|
|
165
|
+
writer: CppWriter | None = None,
|
|
98
166
|
) -> str:
|
|
99
167
|
"""Render one source file as text. ``None`` selects the default one."""
|
|
100
|
-
|
|
168
|
+
self._require_emitted("cpp")
|
|
169
|
+
text = render_cpp(self.build(), cpp_file, writer=self._cpp_writer(writer))
|
|
101
170
|
chosen = self._formatter(formatter)
|
|
102
171
|
if not chosen:
|
|
103
172
|
return text
|
|
@@ -109,9 +178,19 @@ class CppDocBuilder(_MemberScope[CppDoc]):
|
|
|
109
178
|
cpp_files: Sequence[str] | None = None,
|
|
110
179
|
*,
|
|
111
180
|
formatter: Formatter | None = None,
|
|
181
|
+
hpp_writer: HppWriter | None = None,
|
|
182
|
+
cpp_writer: CppWriter | None = None,
|
|
112
183
|
) -> dict[str, str]:
|
|
113
184
|
"""Render every file this document owns, as a name-to-text mapping."""
|
|
114
|
-
return doc_files(
|
|
185
|
+
return doc_files(
|
|
186
|
+
self.build(),
|
|
187
|
+
cpp_files,
|
|
188
|
+
formatter=self._formatter(formatter),
|
|
189
|
+
hpp_writer=self._hpp_writer(hpp_writer),
|
|
190
|
+
cpp_writer=self._cpp_writer(cpp_writer),
|
|
191
|
+
emit_hpp=self.emit_hpp,
|
|
192
|
+
emit_cpp=self.emit_cpp,
|
|
193
|
+
)
|
|
115
194
|
|
|
116
195
|
def write(
|
|
117
196
|
self,
|
|
@@ -119,6 +198,8 @@ class CppDocBuilder(_MemberScope[CppDoc]):
|
|
|
119
198
|
cpp_files: Sequence[str] | None = None,
|
|
120
199
|
*,
|
|
121
200
|
formatter: Formatter | None = None,
|
|
201
|
+
hpp_writer: HppWriter | None = None,
|
|
202
|
+
cpp_writer: CppWriter | None = None,
|
|
122
203
|
skip_unchanged: bool = True,
|
|
123
204
|
encoding: str = "utf-8",
|
|
124
205
|
) -> WriteResult:
|
|
@@ -131,6 +212,10 @@ class CppDocBuilder(_MemberScope[CppDoc]):
|
|
|
131
212
|
directory,
|
|
132
213
|
cpp_files,
|
|
133
214
|
formatter=self._formatter(formatter),
|
|
215
|
+
hpp_writer=self._hpp_writer(hpp_writer),
|
|
216
|
+
cpp_writer=self._cpp_writer(cpp_writer),
|
|
217
|
+
emit_hpp=self.emit_hpp,
|
|
218
|
+
emit_cpp=self.emit_cpp,
|
|
134
219
|
skip_unchanged=skip_unchanged,
|
|
135
220
|
encoding=encoding,
|
|
136
221
|
)
|