apc-model-parser 0.1.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.
- apc_model_parser-0.1.0/.github/workflows/ci.yml +37 -0
- apc_model_parser-0.1.0/.github/workflows/docs.yml +47 -0
- apc_model_parser-0.1.0/.github/workflows/release.yml +50 -0
- apc_model_parser-0.1.0/.gitignore +31 -0
- apc_model_parser-0.1.0/AGENTS.md +379 -0
- apc_model_parser-0.1.0/PKG-INFO +111 -0
- apc_model_parser-0.1.0/README.md +98 -0
- apc_model_parser-0.1.0/docs/api.md +7 -0
- apc_model_parser-0.1.0/docs/chatlogs/2026-06-01_bootstrap-model-parser.md +91 -0
- apc_model_parser-0.1.0/docs/chatlogs/2026-06-01_ci-mkdocs-github-pages-pypi.md +80 -0
- apc_model_parser-0.1.0/docs/decisions/0001-python-cli-with-julia-backend.md +54 -0
- apc_model_parser-0.1.0/docs/decisions/0002-codegen-over-serialized-system.md +56 -0
- apc_model_parser-0.1.0/docs/decisions/0003-explicit-expression-ir.md +53 -0
- apc_model_parser-0.1.0/docs/decisions/0004-target-mtk-v11-idioms.md +61 -0
- apc_model_parser-0.1.0/docs/decisions/0005-cli-verbs-parse-emit.md +55 -0
- apc_model_parser-0.1.0/docs/decisions/index.md +33 -0
- apc_model_parser-0.1.0/docs/deployment/ci-cd.md +65 -0
- apc_model_parser-0.1.0/docs/design/ir-specification.md +126 -0
- apc_model_parser-0.1.0/docs/design/language-strategy.md +83 -0
- apc_model_parser-0.1.0/docs/design/model-parser.md +147 -0
- apc_model_parser-0.1.0/docs/design/storing-mtk-models.md +102 -0
- apc_model_parser-0.1.0/docs/index.md +31 -0
- apc_model_parser-0.1.0/examples/README.md +42 -0
- apc_model_parser-0.1.0/examples/models/model_monod_simple.ini +38 -0
- apc_model_parser-0.1.0/examples/models/model_thermal_tank.ini +52 -0
- apc_model_parser-0.1.0/examples/run.sh +27 -0
- apc_model_parser-0.1.0/julia/ModelParserJL/Project.toml +19 -0
- apc_model_parser-0.1.0/julia/ModelParserJL/README.md +40 -0
- apc_model_parser-0.1.0/julia/ModelParserJL/src/ModelParserJL.jl +114 -0
- apc_model_parser-0.1.0/mkdocs.yml +64 -0
- apc_model_parser-0.1.0/pyproject.toml +52 -0
- apc_model_parser-0.1.0/schemas/canonical-ir.schema.json +529 -0
- apc_model_parser-0.1.0/src/model_parser/__init__.py +18 -0
- apc_model_parser-0.1.0/src/model_parser/backends/__init__.py +5 -0
- apc_model_parser-0.1.0/src/model_parser/backends/julia_mtk.py +159 -0
- apc_model_parser-0.1.0/src/model_parser/cli.py +195 -0
- apc_model_parser-0.1.0/src/model_parser/frontends/__init__.py +9 -0
- apc_model_parser-0.1.0/src/model_parser/frontends/expr_parser.py +183 -0
- apc_model_parser-0.1.0/src/model_parser/frontends/exprtk_ini.py +204 -0
- apc_model_parser-0.1.0/src/model_parser/io.py +48 -0
- apc_model_parser-0.1.0/src/model_parser/ir/__init__.py +55 -0
- apc_model_parser-0.1.0/src/model_parser/ir/expr.py +87 -0
- apc_model_parser-0.1.0/src/model_parser/ir/model.py +133 -0
- apc_model_parser-0.1.0/src/model_parser/schema.py +29 -0
- apc_model_parser-0.1.0/src/model_parser/validation/__init__.py +9 -0
- apc_model_parser-0.1.0/src/model_parser/validation/validators.py +195 -0
- apc_model_parser-0.1.0/tests/conftest.py +44 -0
- apc_model_parser-0.1.0/tests/test_cli.py +73 -0
- apc_model_parser-0.1.0/tests/test_expr_parser.py +66 -0
- apc_model_parser-0.1.0/tests/test_exprtk_ini.py +36 -0
- apc_model_parser-0.1.0/tests/test_julia_mtk.py +61 -0
- apc_model_parser-0.1.0/uv.lock +900 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
python-version: "3.11"
|
|
23
|
+
|
|
24
|
+
- name: Sync environment
|
|
25
|
+
run: uv sync --all-groups
|
|
26
|
+
|
|
27
|
+
- name: Ruff check
|
|
28
|
+
run: uv run ruff check .
|
|
29
|
+
|
|
30
|
+
- name: Ruff format (check only)
|
|
31
|
+
run: uv run ruff format --check .
|
|
32
|
+
|
|
33
|
+
- name: Pytest
|
|
34
|
+
run: uv run pytest
|
|
35
|
+
|
|
36
|
+
- name: MkDocs strict build
|
|
37
|
+
run: uv run mkdocs build --strict
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Publish MkDocs to GitHub Pages (Settings → Pages → Source: GitHub Actions).
|
|
2
|
+
name: Docs
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main, master]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
pages: write
|
|
12
|
+
id-token: write
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: pages
|
|
16
|
+
cancel-in-progress: false
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- uses: astral-sh/setup-uv@v5
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
python-version: "3.11"
|
|
28
|
+
|
|
29
|
+
- name: Sync environment
|
|
30
|
+
run: uv sync --all-groups
|
|
31
|
+
|
|
32
|
+
- name: Build site
|
|
33
|
+
run: uv run mkdocs build --strict
|
|
34
|
+
|
|
35
|
+
- uses: actions/upload-pages-artifact@v3
|
|
36
|
+
with:
|
|
37
|
+
path: site
|
|
38
|
+
|
|
39
|
+
deploy:
|
|
40
|
+
needs: build
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
environment:
|
|
43
|
+
name: github-pages
|
|
44
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
45
|
+
steps:
|
|
46
|
+
- id: deployment
|
|
47
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v[0-9]+.[0-9]+.[0-9]+"
|
|
7
|
+
- "v[0-9]+.[0-9]+.[0-9]+.*"
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build-and-publish:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
environment:
|
|
17
|
+
name: pypi
|
|
18
|
+
url: https://pypi.org/project/apc-model-parser/
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- uses: astral-sh/setup-uv@v5
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
python-version: "3.11"
|
|
26
|
+
|
|
27
|
+
- name: Sync environment
|
|
28
|
+
run: uv sync --all-groups
|
|
29
|
+
|
|
30
|
+
- name: Build sdist and wheel
|
|
31
|
+
run: uv build
|
|
32
|
+
|
|
33
|
+
- name: Upload build artefacts
|
|
34
|
+
uses: actions/upload-artifact@v4
|
|
35
|
+
with:
|
|
36
|
+
name: dist-${{ github.ref_name }}
|
|
37
|
+
path: dist/
|
|
38
|
+
|
|
39
|
+
- name: Create GitHub Release with assets
|
|
40
|
+
uses: softprops/action-gh-release@v2
|
|
41
|
+
with:
|
|
42
|
+
files: dist/*
|
|
43
|
+
generate_release_notes: true
|
|
44
|
+
env:
|
|
45
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
46
|
+
|
|
47
|
+
- name: Publish to PyPI
|
|
48
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
49
|
+
with:
|
|
50
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
|
|
13
|
+
# uv
|
|
14
|
+
# uv.lock is committed; the environment is not.
|
|
15
|
+
|
|
16
|
+
# Julia
|
|
17
|
+
/julia/**/Manifest.toml
|
|
18
|
+
*.jl.cov
|
|
19
|
+
*.jl.*.cov
|
|
20
|
+
*.jl.mem
|
|
21
|
+
|
|
22
|
+
# Generated artifacts (examples write here)
|
|
23
|
+
/examples/outputs/
|
|
24
|
+
|
|
25
|
+
# MkDocs (local build; CI deploys via artifact)
|
|
26
|
+
site/
|
|
27
|
+
|
|
28
|
+
# Editor / OS
|
|
29
|
+
.DS_Store
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# Coding agent guidelines for model-parser
|
|
2
|
+
|
|
3
|
+
This file defines how AI coding agents (e.g. Cursor, Copilot) and human
|
|
4
|
+
contributors collaborate on this repository. Human reviewers should treat it as
|
|
5
|
+
the canonical contributor guide. It is **public**: describe behaviour only in
|
|
6
|
+
terms of this project and its published design — avoid internal codenames or
|
|
7
|
+
unrelated repositories.
|
|
8
|
+
|
|
9
|
+
**Authoritative product specification:**
|
|
10
|
+
[`docs/design/model-parser.md`](docs/design/model-parser.md). When CLI
|
|
11
|
+
semantics, the IR shape, scope, or roadmap change, update that document (and the
|
|
12
|
+
relevant `docs/decisions/NNNN-*.md` ADR) in the same change series as the code.
|
|
13
|
+
|
|
14
|
+
## What this repository is
|
|
15
|
+
|
|
16
|
+
`model-parser` converts process-model definitions **to and from a canonical
|
|
17
|
+
intermediate representation (IR)**. It owns the model-*scaffold* contract: the
|
|
18
|
+
parser, AST, normalizer, IR data model + JSON Schema, validators, and the
|
|
19
|
+
IR→backend lowerings (codegen). It is one tool in the Advanced Process Control
|
|
20
|
+
ecosystem and must remain usable **standalone**.
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
authoring (ExprTk INI) --parse--> AST --normalize--> canonical IR (JSON)
|
|
24
|
+
|
|
|
25
|
+
emit julia --> ModelingToolkit .jl
|
|
26
|
+
emit cpp --> (planned) realtime C++
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Language
|
|
30
|
+
|
|
31
|
+
- **All content in English:** code, comments, docstrings, commit messages,
|
|
32
|
+
user-facing CLI strings, `docs/chatlogs/` entries, and Markdown under `docs/`.
|
|
33
|
+
- Keep domain vocabulary consistent with the org glossary and design docs:
|
|
34
|
+
*scaffold*, *parameter set*, *scenario*, *AST*, *canonical IR*, *profile*,
|
|
35
|
+
*lower*, *export*, *conformance*, *provenance*.
|
|
36
|
+
|
|
37
|
+
## Platform and paths
|
|
38
|
+
|
|
39
|
+
- **Targets:** Linux, macOS, and Windows where the CLI runs. Prefer `pathlib`,
|
|
40
|
+
avoid hard-coded host-specific paths in tests and examples; use `tmp_path` in
|
|
41
|
+
pytest and placeholders in docs (`/tmp`, `example.ini`).
|
|
42
|
+
- **Julia side:** `julia/ModelParserJL/` must remain loadable on the supported
|
|
43
|
+
Julia versions stated in that package; document any extra env assumptions in
|
|
44
|
+
the design doc or `examples/README.md`.
|
|
45
|
+
|
|
46
|
+
## Two languages, one contract
|
|
47
|
+
|
|
48
|
+
| Concern | Home |
|
|
49
|
+
|---|---|
|
|
50
|
+
| CLI, AST, IR data model, JSON Schema, validators, **codegen** | **Python** (`src/model_parser/`) |
|
|
51
|
+
| IR → ModelingToolkit `System`, in-memory build, conformance reference | **Julia** (`julia/ModelParserJL/`) |
|
|
52
|
+
|
|
53
|
+
Both consume the **same** IR JSON. Do not re-implement expression semantics in
|
|
54
|
+
more than one place without IR-level tests. See
|
|
55
|
+
[`docs/decisions/0001-python-cli-with-julia-backend.md`](docs/decisions/0001-python-cli-with-julia-backend.md).
|
|
56
|
+
|
|
57
|
+
## Python tooling — strictly uv
|
|
58
|
+
|
|
59
|
+
Use **uv** for every environment and dependency operation.
|
|
60
|
+
|
|
61
|
+
- `uv add <pkg>` / `uv add --dev <pkg>` to add dependencies.
|
|
62
|
+
- `uv remove <pkg>` to remove them.
|
|
63
|
+
- `uv run <cmd>` to execute commands in the project environment.
|
|
64
|
+
- `uv sync` / `uv sync --all-groups` to recreate the environment from the
|
|
65
|
+
lockfile (use **`--all-groups`** so dev dependencies match CI).
|
|
66
|
+
- **Do not** hand-edit `[project.dependencies]` or `[dependency-groups]` in
|
|
67
|
+
`pyproject.toml`; those are owned by `uv add` / `uv remove`.
|
|
68
|
+
- **Do** edit `[project]` metadata (including `version`), `[project.scripts]`,
|
|
69
|
+
and tool configuration (`[tool.ruff]`, `[tool.pytest.ini_options]`, …).
|
|
70
|
+
|
|
71
|
+
## Code style
|
|
72
|
+
|
|
73
|
+
| Area | Rule |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| Python | 3.11+ per `requires-python` |
|
|
76
|
+
| Linter / formatter | **ruff** (line length **100**, target **py311**) |
|
|
77
|
+
| Type hints | Required on every **public** function and method |
|
|
78
|
+
| Docstrings | Google-flavoured on public APIs; module docstring atop each `.py` |
|
|
79
|
+
| Imports | ruff isort; absolute imports (`model_parser.…`) |
|
|
80
|
+
| Julia | follow the prototype style in `apcplants/ProcessModelingJL`; `Pkg` for deps |
|
|
81
|
+
|
|
82
|
+
Local checks (must match CI):
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv sync --all-groups
|
|
86
|
+
uv run ruff check .
|
|
87
|
+
uv run ruff format --check .
|
|
88
|
+
uv run pytest
|
|
89
|
+
uv run mkdocs build --strict
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Architecture principles
|
|
93
|
+
|
|
94
|
+
### Pure logic vs. I/O separation
|
|
95
|
+
|
|
96
|
+
**Pure modules** parse, normalize, validate, and lower — no filesystem,
|
|
97
|
+
subprocess, or network access. Keep `ir/`, `frontends/`, `backends/`, and
|
|
98
|
+
`validation/` pure and deterministic.
|
|
99
|
+
|
|
100
|
+
**I/O modules** read/write files and implement the CLI. `io.py` and `cli.py` own
|
|
101
|
+
filesystem access; the CLI must stay a thin shell over the pure functions.
|
|
102
|
+
|
|
103
|
+
### One transformation ≈ one module
|
|
104
|
+
|
|
105
|
+
- `frontends/<format>.py` — one authoring format → IR (`parse` + `normalize`).
|
|
106
|
+
- `backends/<target>.py` — IR → one target view (`lower` / codegen).
|
|
107
|
+
- `validation/` — semantic and profile checks.
|
|
108
|
+
- `ir/` — the data model and expression tree; the shared contract.
|
|
109
|
+
|
|
110
|
+
Adding a backend means **one new `backends/*.py` + tests**, never editing every
|
|
111
|
+
other backend (that is the whole point of the hub-and-spoke IR).
|
|
112
|
+
|
|
113
|
+
### The IR is the single semantic truth
|
|
114
|
+
|
|
115
|
+
- The IR describes a **scaffold only** (structure + equations + roles + units +
|
|
116
|
+
metadata). Parameter sets and scenarios (incl. initial values `x0`/`u0`) are
|
|
117
|
+
**out of scope** — frontends drop them with a `WARN`.
|
|
118
|
+
- The IR is **versioned JSON** (`ir_version`, SemVer). A breaking schema change
|
|
119
|
+
requires migration notes and an ADR.
|
|
120
|
+
- Every IR carries a **content hash** over its semantic body; downstream
|
|
121
|
+
artifacts reference scaffolds by hash, not path.
|
|
122
|
+
- Expression semantics live in the **explicit expression tree**
|
|
123
|
+
(`ir/expr.py`), never in per-backend string rewrites.
|
|
124
|
+
|
|
125
|
+
### Codegen, not serialized objects
|
|
126
|
+
|
|
127
|
+
The durable form of an MTK model is the **IR JSON + generated `.jl` script**,
|
|
128
|
+
never a serialized `System`. Generated Julia targets **MTK v11** idioms
|
|
129
|
+
(`System(eqs, t)`, `mtkcompile`, `t_nounits`/`D_nounits`, no `@mtkmodel`, no
|
|
130
|
+
`structural_simplify`). See ADRs 0002 and 0004.
|
|
131
|
+
|
|
132
|
+
### Determinism & idempotency
|
|
133
|
+
|
|
134
|
+
Re-running `parse`/`emit` on unchanged input must produce byte-identical output
|
|
135
|
+
(stable ordering, stable number formatting). The content hash depends only on
|
|
136
|
+
the semantic body.
|
|
137
|
+
|
|
138
|
+
### Status vocabulary and exit codes
|
|
139
|
+
|
|
140
|
+
CLI diagnostics use `OK` · `WARN` · `ERROR`. Exit codes:
|
|
141
|
+
|
|
142
|
+
| Code | Meaning |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `0` | success; no `ERROR` diagnostics |
|
|
145
|
+
| `1` | at least one `ERROR` (e.g. validation failed) |
|
|
146
|
+
| `2` | invalid usage / load failure before meaningful work |
|
|
147
|
+
|
|
148
|
+
### CLI conventions
|
|
149
|
+
|
|
150
|
+
- Long options in kebab-case (`--from`, `--profile`, `--output`/`-o`).
|
|
151
|
+
- The two core verbs are `parse` (authoring → IR) and `emit <target>` (IR →
|
|
152
|
+
view). Supporting commands: `validate`, `inspect`, `ast`, `schema`.
|
|
153
|
+
- Breaking CLI changes require a SemVer bump and release notes.
|
|
154
|
+
|
|
155
|
+
## Scope guardrails
|
|
156
|
+
|
|
157
|
+
**In scope:** authoring-format parsing, the canonical IR + JSON Schema, semantic
|
|
158
|
+
& profile validation, IR↔backend lowering / codegen, conformance fixtures.
|
|
159
|
+
|
|
160
|
+
**Out of scope:** parameter identification, scenario execution / simulation,
|
|
161
|
+
result storage, controller synthesis, deployment, UI. Those are sibling tools
|
|
162
|
+
that *consume* the IR.
|
|
163
|
+
|
|
164
|
+
If a change request conflicts with the design doc, stop and resolve via an
|
|
165
|
+
explicit doc + ADR update — do not silently expand scope.
|
|
166
|
+
|
|
167
|
+
## Work tracking (ADRs and issues)
|
|
168
|
+
|
|
169
|
+
- **ADRs:** numbered files under [`docs/decisions/`](docs/decisions/) (`NNNN-title.md`),
|
|
170
|
+
indexed in [`docs/decisions/index.md`](docs/decisions/index.md). One decision per
|
|
171
|
+
file; never delete — supersede with a newer ADR and update the index.
|
|
172
|
+
- **Issues:** use GitHub issues for backlog items that are not yet an ADR; link
|
|
173
|
+
ADRs from issue/PR text when a decision motivates the change.
|
|
174
|
+
- Prefer **small, reviewable slices** (one coherent feature, bugfix, or refactor
|
|
175
|
+
per PR when practical). For larger work, split PRs and reference the same ADR
|
|
176
|
+
or design section in each.
|
|
177
|
+
|
|
178
|
+
### Recommended slice workflow (maintainers & agents)
|
|
179
|
+
|
|
180
|
+
1. **Align scope** — confirm design doc + ADR if behaviour or IR contract changes.
|
|
181
|
+
2. **Implement & test** — code and automated tests for that slice; keep pure vs I/O
|
|
182
|
+
boundaries intact.
|
|
183
|
+
3. **Verify locally** — `uv sync --all-groups`, ruff check/format, `uv run pytest`,
|
|
184
|
+
`uv run mkdocs build --strict` (same as CI).
|
|
185
|
+
4. **Docs** — update `docs/design/model-parser.md`, schema regeneration command, or
|
|
186
|
+
ADR in the **same** change series when the public contract moves.
|
|
187
|
+
5. **`docs/chatlogs/…`** when the slice materially changes behaviour or governance.
|
|
188
|
+
|
|
189
|
+
## Testing
|
|
190
|
+
|
|
191
|
+
- Framework: **pytest** (`uv run pytest`).
|
|
192
|
+
- **Unit tests** for pure logic: expression parser, frontend, backend codegen,
|
|
193
|
+
validators.
|
|
194
|
+
- **CLI smoke tests** with `typer.testing.CliRunner`.
|
|
195
|
+
- **Conformance** (as it grows): an IR fixture plus expected Julia output and/or
|
|
196
|
+
expected trajectories, shared between the Python codegen and `ModelParserJL`.
|
|
197
|
+
|
|
198
|
+
## Documentation system
|
|
199
|
+
|
|
200
|
+
**Target stack** (align when a docs site is added): **MkDocs** with the **Material**
|
|
201
|
+
theme and **mkdocstrings** (or equivalent) for Python API reference; Julia API
|
|
202
|
+
docs may stay in `julia/ModelParserJL/` README until unified.
|
|
203
|
+
|
|
204
|
+
**Intended layout** (keep consistent with [`docs/design/model-parser.md`](docs/design/model-parser.md)):
|
|
205
|
+
|
|
206
|
+
- `docs/index.md` or root `README.md` — overview, install (`uv`), quick `parse` /
|
|
207
|
+
`emit` examples (when expanded).
|
|
208
|
+
- `docs/design/` — product spec (`model-parser.md`) and deep architecture notes.
|
|
209
|
+
- `docs/decisions/` — ADRs (`NNNN-title.md`) + `index.md`.
|
|
210
|
+
- `docs/chatlogs/` — session summaries (see **Session summaries** below).
|
|
211
|
+
- `docs/deployment/` — optional summaries for CI/CD, packaging, and release
|
|
212
|
+
process once those exist in-repo.
|
|
213
|
+
|
|
214
|
+
**Rules:**
|
|
215
|
+
|
|
216
|
+
- User-facing prose lives under `docs/`; IR semantics and the CLI contract stay
|
|
217
|
+
accurate relative to the design doc and JSON Schema.
|
|
218
|
+
- Do not commit secrets, real tokens, or customer-specific identifiers; use
|
|
219
|
+
placeholders (`example.com`, generic model names).
|
|
220
|
+
- Once `mkdocs.yml` exists, CI **should** run `uv run mkdocs build --strict`; until
|
|
221
|
+
then, Markdown in `docs/` remains valid without a site build.
|
|
222
|
+
|
|
223
|
+
**Generated artefacts:**
|
|
224
|
+
|
|
225
|
+
- [`schemas/canonical-ir.schema.json`](schemas/canonical-ir.schema.json) is
|
|
226
|
+
committed output; regenerate with
|
|
227
|
+
`uv run model-parser schema -o schemas/canonical-ir.schema.json`
|
|
228
|
+
whenever the IR Pydantic models change, in the same change series as the code.
|
|
229
|
+
|
|
230
|
+
## Runnable examples (`examples/`)
|
|
231
|
+
|
|
232
|
+
- Keep **small, well-commented, copy-paste-friendly** inputs under
|
|
233
|
+
[`examples/models/`](examples/models/) and expected outputs under
|
|
234
|
+
[`examples/outputs/`](examples/outputs/) where helpful for reviewers and users
|
|
235
|
+
(not only pytest).
|
|
236
|
+
- Prefer **English** comments and placeholders — never real proprietary model text
|
|
237
|
+
or secrets.
|
|
238
|
+
- When a session ships **user-visible CLI or IR** behaviour, add or refresh an
|
|
239
|
+
**`examples/…`** entry in the **same** merge series as the chatlog when feasible.
|
|
240
|
+
- Document how to run examples in [`examples/README.md`](examples/README.md)
|
|
241
|
+
(e.g. `examples/run.sh`, `uv run model-parser …`).
|
|
242
|
+
|
|
243
|
+
## Session summaries (`docs/chatlogs/`)
|
|
244
|
+
|
|
245
|
+
**Required** after substantive sessions (multi-step implementation, non-trivial
|
|
246
|
+
design discussion, or when behaviour or doc intent shifts).
|
|
247
|
+
|
|
248
|
+
### Commit helper (mandatory placement)
|
|
249
|
+
|
|
250
|
+
Every chatlog MUST begin with a **`## Commit helper`** section (level-2 heading).
|
|
251
|
+
|
|
252
|
+
**Order of file contents**
|
|
253
|
+
|
|
254
|
+
1. **YAML frontmatter** (when used) MUST be the **first bytes** of the file (`---` … `---`).
|
|
255
|
+
2. Immediately after the closing `---`, **`## Commit helper`** MUST be the **first
|
|
256
|
+
Markdown body content** — nothing above it except frontmatter.
|
|
257
|
+
3. Next, **`## How to try`** MUST follow **`## Commit helper`** (not the long narrative).
|
|
258
|
+
4. If frontmatter is omitted, **`## Commit helper`** is the first line, then **`## How to try`**, then the narrative.
|
|
259
|
+
|
|
260
|
+
**Commit helper MUST contain**
|
|
261
|
+
|
|
262
|
+
- **`SemVer / version bump`:** **no bump** vs **PATCH / MINOR / MAJOR**, tied to
|
|
263
|
+
what shipped (docs-only vs user-visible CLI/IR vs breaking schema or CLI). See
|
|
264
|
+
[**Versioning, releases, and release notes**](#versioning-releases-and-release-notes).
|
|
265
|
+
- **`Tags / GitHub Release`:** always fill — **None** (stack on default branch) or
|
|
266
|
+
**Tag after bump** with exact tag string (e.g. `v0.2.0`), pre-release if needed,
|
|
267
|
+
and whether a **`release.yml`** (or future) workflow should run on tag push.
|
|
268
|
+
- **Suggested commit message:** one imperative subject; optional Conventional scope
|
|
269
|
+
(`feat(cli): …`, `fix(ir): …`). Use `BREAKING CHANGE:` footer when applicable.
|
|
270
|
+
- **Copy-paste git commands:** fenced `bash` with `git add` paths and
|
|
271
|
+
`git commit -m '…'`. If the version bump belongs in the same commit, note updates
|
|
272
|
+
to `pyproject.toml` and `src/model_parser/__init__.py`.
|
|
273
|
+
- **Git order when tagging:** never tag before the release commit exists locally:
|
|
274
|
+
**commit / push branch (with version bump)** → **`git tag -a vX.Y.Z` on that
|
|
275
|
+
commit** → **`git push origin vX.Y.Z`**.
|
|
276
|
+
|
|
277
|
+
### How to try (mandatory unless N/A)
|
|
278
|
+
|
|
279
|
+
Immediately after **`## Commit helper`**, include **`## How to try`** with concrete
|
|
280
|
+
`uv run model-parser …` steps, pytest selection, or `examples/…` commands. For
|
|
281
|
+
pure docs/chatlog hygiene: **`N/A (docs-only).`**
|
|
282
|
+
|
|
283
|
+
### Session narrative
|
|
284
|
+
|
|
285
|
+
After **How to try**, summarize shipped modules, CLI/IR edge cases, and regression
|
|
286
|
+
risks in English (**goal → shipped surface → decisions → follow-ups**).
|
|
287
|
+
|
|
288
|
+
**Naming:** `YYYY-MM-DD_short-topic.md` (ASCII slug).
|
|
289
|
+
|
|
290
|
+
**Frontmatter (YAML):** at least `title`, `topic`, `date_added`, `tags: [chatlogs]`,
|
|
291
|
+
and `links:` to touched specs or code (e.g. `AGENTS.md`, `docs/design/model-parser.md`).
|
|
292
|
+
|
|
293
|
+
**Not a substitute for:** ADRs, tests, or commit messages — those remain the source
|
|
294
|
+
of truth for the contract.
|
|
295
|
+
|
|
296
|
+
## CI/CD (GitHub Actions)
|
|
297
|
+
|
|
298
|
+
Workflows live under [`.github/workflows/`](.github/workflows/). Maintainer-facing
|
|
299
|
+
detail (Pages setup, PyPI trusted publishing, tag order) is in
|
|
300
|
+
[`docs/deployment/ci-cd.md`](docs/deployment/ci-cd.md).
|
|
301
|
+
|
|
302
|
+
| Workflow | Trigger | Purpose |
|
|
303
|
+
| --- | --- | --- |
|
|
304
|
+
| [`ci.yml`](.github/workflows/ci.yml) | Push / PR to `main` or `master` | `uv sync --all-groups`, ruff, pytest, **`mkdocs build --strict`** |
|
|
305
|
+
| [`docs.yml`](.github/workflows/docs.yml) | Push to `main`/`master`, `workflow_dispatch` | Build MkDocs and deploy to **GitHub Pages** |
|
|
306
|
+
| [`release.yml`](.github/workflows/release.yml) | Push SemVer tags `vX.Y.Z` (and pre-release suffixes) | `uv build`, GitHub Release + assets, **PyPI** (OIDC) |
|
|
307
|
+
|
|
308
|
+
**Rules:**
|
|
309
|
+
|
|
310
|
+
- CI commands must be reproducible locally (`uv run …`); the **local check block**
|
|
311
|
+
above must stay aligned with `ci.yml`.
|
|
312
|
+
- **PyPI** uses **trusted publishing** via `pypa/gh-action-pypi-publish` with GitHub
|
|
313
|
+
**environment** `pypi` — configure the same environment name on PyPI’s pending
|
|
314
|
+
publisher (or adjust the workflow if PyPI omits environment). Distribution name:
|
|
315
|
+
**`apc-model-parser`**; import package and CLI: **`model_parser`** / `model-parser`.
|
|
316
|
+
- **GitHub Pages:** repository **Settings → Pages → Source: GitHub Actions** (one-time).
|
|
317
|
+
Published site: `https://advanced-process-control.github.io/model-parser/` (must
|
|
318
|
+
match `site_url` in `mkdocs.yml`).
|
|
319
|
+
|
|
320
|
+
## Versioning, releases, and release notes
|
|
321
|
+
|
|
322
|
+
### Semantic versioning
|
|
323
|
+
|
|
324
|
+
Follow **[Semantic Versioning 2.0.0](https://semver.org/)** (`MAJOR.MINOR.PATCH`).
|
|
325
|
+
|
|
326
|
+
- **`0.y.z`:** breaking CLI or IR schema changes are allowed but must be called out
|
|
327
|
+
in release notes and usually accompanied by an ADR.
|
|
328
|
+
- **`1.0.0` onward:** honour SemVer for the **documented** public surface: stable
|
|
329
|
+
CLI flags, `ir_version`, JSON Schema compatibility as advertised.
|
|
330
|
+
|
|
331
|
+
### Single source of truth for the running version
|
|
332
|
+
|
|
333
|
+
Bump **both** in the same release series until tooling ties them automatically:
|
|
334
|
+
|
|
335
|
+
- `project.version` in [`pyproject.toml`](pyproject.toml)
|
|
336
|
+
- `__version__` in [`src/model_parser/__init__.py`](src/model_parser/__init__.py)
|
|
337
|
+
|
|
338
|
+
Keep them identical for each release tag.
|
|
339
|
+
|
|
340
|
+
### Tags and GitHub Releases
|
|
341
|
+
|
|
342
|
+
- **Ordering:** merge the commit(s) that bump version fields; **then** create an
|
|
343
|
+
**annotated** tag **`vX.Y.Z`** on **that** commit; **then** `git push origin vX.Y.Z`.
|
|
344
|
+
Release automation (when added) should trigger on tag push; the leading `v` is
|
|
345
|
+
a common convention — match whatever `release.yml` expects.
|
|
346
|
+
- **Release notes:** use GitHub auto-generated notes where helpful; edit for IR or
|
|
347
|
+
CLI highlights.
|
|
348
|
+
- **Optional `CHANGELOG.md`:** if introduced, follow
|
|
349
|
+
[Keep a Changelog](https://keepachangelog.com/) and update it in the release
|
|
350
|
+
commit series.
|
|
351
|
+
|
|
352
|
+
### Artefacts
|
|
353
|
+
|
|
354
|
+
- **`uv build`** produces `dist/*.tar.gz` and `dist/*.whl`. A release workflow can
|
|
355
|
+
attach them to GitHub Releases and PyPI.
|
|
356
|
+
- **Wheel tags** and `requires-python` must match actual dependency and runtime
|
|
357
|
+
support; adjust classifiers in `pyproject.toml` as the project matures.
|
|
358
|
+
|
|
359
|
+
### Pre-releases
|
|
360
|
+
|
|
361
|
+
Pre-release tags (e.g. `v0.2.0-rc.1`, PEP 440–compatible) are fine for testers.
|
|
362
|
+
Mark GitHub Releases as **pre-release** when behaviour or schema is unstable.
|
|
363
|
+
|
|
364
|
+
## Security
|
|
365
|
+
|
|
366
|
+
- Never commit secrets (tokens, private URLs with embedded credentials).
|
|
367
|
+
- Logs and user-visible errors must not echo full file contents of untrusted inputs
|
|
368
|
+
at high verbosity without care — follow the design doc for diagnostic redaction
|
|
369
|
+
policy as it evolves.
|
|
370
|
+
- Generated Julia/C++ must not embed secrets from authoring files; treat IR JSON as
|
|
371
|
+
shareable engineering data unless the org classifies it otherwise.
|
|
372
|
+
|
|
373
|
+
## Commit conventions
|
|
374
|
+
|
|
375
|
+
- Imperative, concise subjects (`add mtk v11 emit path`, not `added path`).
|
|
376
|
+
- Prefer one logical change per commit.
|
|
377
|
+
- Reference GitHub issues or ADR filenames when it aids traceability.
|
|
378
|
+
- [Conventional Commits](https://www.conventionalcommits.org/) shape where it
|
|
379
|
+
helps (`feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `ci`).
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apc-model-parser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Convert process-model definitions to and from a canonical intermediate representation.
|
|
5
|
+
Project-URL: Repository, https://github.com/Advanced-Process-Control/model-parser
|
|
6
|
+
Author: Advanced Process Control
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: codegen,exprtk,intermediate-representation,modelingtoolkit,process-model
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Requires-Dist: pydantic>=2.6
|
|
11
|
+
Requires-Dist: typer>=0.12
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# model-parser
|
|
15
|
+
|
|
16
|
+
**Convert process-model definitions to and from a canonical intermediate
|
|
17
|
+
representation (IR).**
|
|
18
|
+
|
|
19
|
+
`model-parser` is the Advanced Process Control toolbox component that owns the
|
|
20
|
+
*model scaffold* contract and the transformations around it. It parses an
|
|
21
|
+
authoring format (today: the ExprTk-style INI used by the MPC / simulation
|
|
22
|
+
toolchain) into a normalized, backend-independent **canonical IR**, and lowers
|
|
23
|
+
that IR into target views — starting with a generated **ModelingToolkit (Julia)**
|
|
24
|
+
model script.
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
authoring (ExprTk INI) --parse--> AST --normalize--> canonical IR (JSON)
|
|
28
|
+
|
|
|
29
|
+
emit julia --> ModelingToolkit .jl
|
|
30
|
+
emit cpp --> (planned) realtime C++
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The IR is the single semantic contract. Adding a backend means writing one
|
|
34
|
+
`lower` + one `export`, not an N×N mesh of view-to-view translators. See
|
|
35
|
+
[`docs/design/model-parser.md`](docs/design/model-parser.md) for the
|
|
36
|
+
authoritative product specification and
|
|
37
|
+
[`docs/decisions/`](docs/decisions/) for the design decision records.
|
|
38
|
+
|
|
39
|
+
## Why two languages?
|
|
40
|
+
|
|
41
|
+
| Concern | Home | Why |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| CLI, AST, IR, JSON Schema, validators, **codegen** | **Python** (this package) | Parsing & orchestration strength; no symbolic runtime needed to emit code. |
|
|
44
|
+
| IR → MTK `System`, simulation, analysis, conformance | **Julia** ([`julia/ModelParserJL`](julia/ModelParserJL/)) | Natural fit for ModelingToolkit; reference for parity tests. |
|
|
45
|
+
|
|
46
|
+
The Python CLI builds and validates the IR and **generates** Julia code; the
|
|
47
|
+
Julia package can additionally load an IR directly into an MTK `System` for
|
|
48
|
+
in-memory, dynamic workflows. Both consume the *same* IR. See
|
|
49
|
+
[ADR 0001](docs/decisions/0001-python-cli-with-julia-backend.md).
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
This project uses **[uv](https://docs.astral.sh/uv/)** for everything.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv sync --all-groups # include dev tools (ruff, pytest, mkdocs)
|
|
57
|
+
uv run model-parser --help
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## CLI
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# 1. ExprTk INI -> canonical IR JSON
|
|
64
|
+
uv run model-parser parse examples/models/model_monod_simple.ini -o monod.ir.json
|
|
65
|
+
|
|
66
|
+
# 2. canonical IR -> ModelingToolkit (v11) Julia model
|
|
67
|
+
uv run model-parser emit julia monod.ir.json -o monod.jl
|
|
68
|
+
|
|
69
|
+
# Supporting commands
|
|
70
|
+
uv run model-parser validate monod.ir.json --profile julia-analysis
|
|
71
|
+
uv run model-parser inspect monod.ir.json
|
|
72
|
+
uv run model-parser ast examples/models/model_monod_simple.ini # debug tree
|
|
73
|
+
uv run model-parser schema -o schemas/canonical-ir.schema.json # export schema
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`parse` accepts `--from exprtk-ini` (default). `validate` accepts either an IR
|
|
77
|
+
`.json` file or an INI file (parsed on the fly). Exit codes: `0` success,
|
|
78
|
+
`1` validation errors, `2` usage / load failure.
|
|
79
|
+
|
|
80
|
+
## How "stored MTK models" work
|
|
81
|
+
|
|
82
|
+
The persisted, version-controlled form of a model is the **IR JSON** plus the
|
|
83
|
+
**generated `.jl` script** — *not* a serialized `System` object. ModelingToolkit
|
|
84
|
+
v11 changed `System` internals significantly (precompilation, removal of
|
|
85
|
+
`defaults`, deprecation of `@mtkmodel`), so serializing the live object is
|
|
86
|
+
brittle across versions. Regenerating from the IR is the durable path. See
|
|
87
|
+
[ADR 0002](docs/decisions/0002-codegen-over-serialized-system.md) and
|
|
88
|
+
[`docs/design/storing-mtk-models.md`](docs/design/storing-mtk-models.md).
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv sync --all-groups
|
|
94
|
+
uv run ruff check .
|
|
95
|
+
uv run ruff format --check .
|
|
96
|
+
uv run pytest
|
|
97
|
+
uv run mkdocs build --strict # same as CI
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Documentation site (after [Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow) is set to GitHub Actions):
|
|
101
|
+
[https://advanced-process-control.github.io/model-parser/](https://advanced-process-control.github.io/model-parser/)
|
|
102
|
+
|
|
103
|
+
## Scope
|
|
104
|
+
|
|
105
|
+
**In scope:** authoring-format parsing, the canonical IR data model + JSON
|
|
106
|
+
Schema, semantic & profile validation, IR↔backend lowering/codegen.
|
|
107
|
+
|
|
108
|
+
**Out of scope:** parameter identification, scenario execution, simulation
|
|
109
|
+
result storage, controller synthesis, deployment. Those are sibling tools that
|
|
110
|
+
*consume* the IR. The parser stays small (see
|
|
111
|
+
[`AGENTS.md`](AGENTS.md) scope guardrails).
|