Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Construct Header, Source files from an AST-like structure instead of Jinja templating #13

Merged
merged 52 commits into from Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
427e06d
WIP: Build up code structure in an AST style
buckbaskin May 7, 2023
7cb9e88
Struct definitions V1
buckbaskin May 7, 2023
c741547
State V1
buckbaskin May 7, 2023
3e39123
Covariance V1
buckbaskin May 7, 2023
0c8ce11
Control V1
buckbaskin May 7, 2023
ba9058e
Moved to AST generation feature test
buckbaskin May 7, 2023
6e79412
Feature test exposed that I forgot my closing ; for classes
buckbaskin May 7, 2023
d0b9689
Add short function params, but just put body on newline
buckbaskin May 7, 2023
e43d6ee
Huh? Featuretests show that it's passing...
buckbaskin May 7, 2023
1f05aa9
WIP: Tests demonstrating enum class, forward declaration not supported
buckbaskin May 7, 2023
d0f6cc8
enum class, forward declaration
buckbaskin May 7, 2023
344bf99
WIP: need free functions
buckbaskin May 7, 2023
89434aa
WIP: static function declarations
buckbaskin May 7, 2023
f2dba25
Better modifier support for functions
buckbaskin May 7, 2023
b8c1a88
WIP: Last 2 features need passing, one passed with quick work
buckbaskin May 9, 2023
f88af85
More progress on representing templated types
buckbaskin May 9, 2023
6f1f50e
Support templating, still not fully done with body implementations
buckbaskin May 9, 2023
702c615
Fix Extended Kalman Filter Process Model test
buckbaskin May 30, 2023
381dbc1
WIP: BUILD changes to generate CPP feature test
buckbaskin Jun 1, 2023
2bf904a
Red: Bazel coordinating things, but test not compiling with AST gener…
buckbaskin Jun 1, 2023
88008b1
Red: WIP, builds up to no implementation of the constructor (as expec…
buckbaskin Jun 1, 2023
d9b316b
Green: The smallest possible test compiles and passes
buckbaskin Jun 1, 2023
25a679d
Red: Issue with the multi-arg function, updated py test
buckbaskin Jun 1, 2023
da36a61
WIP: Working up to the State constructor not doing the right thing fo…
buckbaskin Jun 3, 2023
9f0f8d0
Green: Generation aligned with Cpp test
buckbaskin Jun 3, 2023
c16b5f1
Move py testing to unit tests
buckbaskin Jun 3, 2023
0141de1
Reorganize/subdivide py/test
buckbaskin Jun 3, 2023
490f2ce
Update actions/format.bash
buckbaskin Jun 3, 2023
34cd8f4
Now generate some AST elements from template
buckbaskin Jun 3, 2023
80da3d1
Green-ish: First test passes with new generation
buckbaskin Jun 3, 2023
2df78ee
WIP
buckbaskin Jun 4, 2023
e8714b0
Generate Calibration structure for Model
buckbaskin Jun 5, 2023
60e6c33
WIP
buckbaskin Jun 5, 2023
c02de2c
WIP: Generate much of the EKF header
buckbaskin Jun 5, 2023
3f33ff9
Greenish: one EKF test passes with new header. 2 failing tests
buckbaskin Jun 5, 2023
13fc1ae
Green: Tests passing
buckbaskin Jun 5, 2023
ba2ae64
Refactor: Remove some unused code
buckbaskin Jun 5, 2023
7b1006a
Refactor: Add missing required optional include
buckbaskin Jun 5, 2023
87e4b9b
WIP: Implementing the source file via AST
buckbaskin Jun 6, 2023
473ecb8
Merge branch 'COnDE-nAST' of github.com:buckbaskin/formak into COnDE-…
buckbaskin Jun 6, 2023
089f810
WIP: Model with control source working
buckbaskin Jun 8, 2023
dc484f6
WIP: Most non-sensor functionality for EKF
buckbaskin Jun 8, 2023
e29d84a
Green: AST generation for source is passing tests
buckbaskin Jun 8, 2023
dc3302d
Refactor: Simplify argparse with modified template lookup
buckbaskin Jun 8, 2023
00ac1ba
Refactor: Remove unused bulk templates
buckbaskin Jun 8, 2023
9225e85
Refactor: Clean up cpp.py
buckbaskin Jun 8, 2023
abf68fd
Move to typed ExtrasT
buckbaskin Jun 8, 2023
0a0a975
Use generator instances directly for the extras
buckbaskin Jun 8, 2023
5326822
WIP: Moving away from inserts
buckbaskin Jun 13, 2023
8f759e2
Refactor: Removing top level inserts
buckbaskin Jun 13, 2023
fbbb6cd
Add design doc
buckbaskin Jun 13, 2023
1416bbe
Add AST tools docs
buckbaskin Jun 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Expand Up @@ -8,7 +8,7 @@ repos:
- id: check-ast
- id: check-byte-order-marker
- id: check-case-conflict
- id: check-docstring-first
# - id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-json
- id: check-merge-conflict
Expand Down
5 changes: 4 additions & 1 deletion actions/format.bash
Expand Up @@ -15,7 +15,10 @@ echo "black"
black $DEFAULT ;

echo "isort"
isort --profile black py/ ;
isort --profile black py/ featuretests/;

echo "codespell"
codespell py/ featuretests/

echo "clang-format"
SEARCHRESULT=$(ag --cpp -g ".*" $DEFAULT) ;
Expand Down
105 changes: 105 additions & 0 deletions docs/designs/generate_cpp_from_ast.md
@@ -0,0 +1,105 @@
# Generate C++ from AST

Author: Buck Baskin @bebaskin
Created: 2023-06-12
Updated: 2023-06-12
Parent Design: [designs/cpp_library_for_model_evaluation.md](../designs/cpp_library_for_model_evaluation.md)
Status: Refactor/Cleanup

## Overview

FormaK aims to combine symbolic modeling for fast, efficient system modelling
with code generation to create performant code that is easy to use.

This design provides an extension to the fifth of the Five Keys
"C++ interfaces to support a variety of model uses" by reworking how C++
generation is done for easier extensions. After the Calibration design, a lot
of the code templates looked like:

```
StateAndVariance
ExtendedKalmanFilter::process_model(
double dt,
const StateAndVariance& input
// clang-format off
{% if enable_calibration %}
// clang-format on
,
const Calibration& input_calibration
// clang-format off
{% endif %} // clang-format on
// clang-format off
{% if enable_control %}
// clang-format on
,
const Control& input_control
// clang-format off
{% endif %} // clang-format on
) {
```

Instead of relying on increasingly intricate Jinja templating and managing
formatting via flagging clang-format on and off, I instead opted for another
approach: generate the code from an AST that approximated the Python AST. The
reason to go with something that approximates the Python AST is to have an
inspiration and a guide from an AST that has accumulated experience.

Afterwards, the code can look like:

```
args = [
Arg("double", "dt"),
Arg("const StateAndVariance&", "input_state"),
]


if enable_calibration:
args.append(Arg("const Calibration&", "input_calibration"))
if enable_control:
args.append(Arg("const Control&", "input_control"))

return FunctionDeclaration(
"StateAndVariance",
"process_model",
args=args,
modifier="",
)
```

This approach isn't necessarily shorter, but it allows for replacing Jinja
templating with manipulating Python structures (primarily lists) in code. It
also generates cleaner code without droppings for clang-formatting

## Feature Tests

The feature tests for this were originally based on generating code to match
strings of examples purely in Python. Eventually, they were moved to C++
compilation to capture ensuring the overall feature generated valid C++.

## Road Map and Process

1. Write a design
2. Write a feature test(s)
3. Build a simple prototype
4. Pass feature tests
5. Refactor/cleanup
6. Build an instructive prototype (e.g. something that looks like the project vision but doesn’t need to be the full thing)
7. Add unit testing, etc
8. Refactor/cleanup
9. Write up successes, retro of what changed (so I can check for this in future designs)

## Post Review

### 2023-06-12

Overall, this design was a lot of manual work to translate over. I remain
optimistic that this translation will be worth it.

There are some areas where the learnings evolved over the project. Primarily,
this was the patterns for concisely and clearly manipulating the structures as
they were being implemented, especially args. Things evolved through:

- copying and pasting code
- wrapping the logic in functions, but still with repeated code (see `State_model`)
- finding the `standard_args` pattern (see the `ClassDef` for `{reading_type.typename}SensorModel`)
- In theory, this could go to filtering args based on zipping with an enable value, but I haven't gone to this yet (some of the other functional changes got quite long and full of parens)
22 changes: 22 additions & 0 deletions docs/formak/ast_tools.md
@@ -0,0 +1,22 @@
# AST Tools

# For Users

Start with either a `HeaderFile` or `SourceFile`, along with at least one
`namespace`.

Then keep building up the tree structure recursively.

If you don't think something is supported, I'd recommend starting with
`FromFileTemplate`. If you need to insert an arbitrary string, you can pass it
wrapped via `Escape`.

# For Contributors

Every class should inherit from BaseAst

Future Extensions:
- Visitor, Editor patterns for viewing or building the tree
- Split out statements and expressions
- Split out constructs that can only be used in a header file, source file or both
- Split out constructs based on what scopes they can be used
6 changes: 6 additions & 0 deletions experimental/BUILD
Expand Up @@ -193,3 +193,9 @@ py_binary(
srcs = ["src/linear_cpu_model.py"],
main = "src/linear_cpu_model.py",
)

py_binary(
name = "ast-compile",
srcs = ["src/ast_compile.py"],
main = "src/ast_compile.py",
)