isar-tools 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.
- isar_tools-0.1.0/.gitignore +9 -0
- isar_tools-0.1.0/CHANGELOG.md +80 -0
- isar_tools-0.1.0/LICENSE +21 -0
- isar_tools-0.1.0/PKG-INFO +230 -0
- isar_tools-0.1.0/README.md +209 -0
- isar_tools-0.1.0/docs/FORMATTER.md +127 -0
- isar_tools-0.1.0/docs/PARSER_DECISION.md +194 -0
- isar_tools-0.1.0/docs/PLAN.md +302 -0
- isar_tools-0.1.0/docs/RELEASING.md +99 -0
- isar_tools-0.1.0/pyproject.toml +69 -0
- isar_tools-0.1.0/src/isar_tools/__init__.py +1 -0
- isar_tools-0.1.0/src/isar_tools/__main__.py +5 -0
- isar_tools-0.1.0/src/isar_tools/checks/__init__.py +0 -0
- isar_tools-0.1.0/src/isar_tools/checks/cli.py +156 -0
- isar_tools-0.1.0/src/isar_tools/checks/docs.py +114 -0
- isar_tools-0.1.0/src/isar_tools/checks/findings.py +55 -0
- isar_tools-0.1.0/src/isar_tools/checks/locales.py +370 -0
- isar_tools-0.1.0/src/isar_tools/checks/project.py +97 -0
- isar_tools-0.1.0/src/isar_tools/checks/theory.py +117 -0
- isar_tools-0.1.0/src/isar_tools/cli.py +55 -0
- isar_tools-0.1.0/src/isar_tools/formatter/__init__.py +0 -0
- isar_tools-0.1.0/src/isar_tools/formatter/cli.py +103 -0
- isar_tools-0.1.0/src/isar_tools/formatter/formatter.py +377 -0
- isar_tools-0.1.0/src/isar_tools/formatter/wrap.py +103 -0
- isar_tools-0.1.0/src/isar_tools/project/__init__.py +0 -0
- isar_tools-0.1.0/src/isar_tools/project/cli.py +548 -0
- isar_tools-0.1.0/src/isar_tools/project/hierarchy.py +330 -0
- isar_tools-0.1.0/src/isar_tools/project/model.py +353 -0
- isar_tools-0.1.0/src/isar_tools/project/names.py +246 -0
- isar_tools-0.1.0/src/isar_tools/project/root.py +298 -0
- isar_tools-0.1.0/src/isar_tools/project/workspace.py +125 -0
- isar_tools-0.1.0/src/isar_tools/render.py +110 -0
- isar_tools-0.1.0/src/isar_tools/source/__init__.py +0 -0
- isar_tools-0.1.0/src/isar_tools/source/files.py +16 -0
- isar_tools-0.1.0/src/isar_tools/source/keywords.py +197 -0
- isar_tools-0.1.0/src/isar_tools/source/lexer.py +206 -0
- isar_tools-0.1.0/src/isar_tools/source/symbol_table.py +448 -0
- isar_tools-0.1.0/src/isar_tools/source/symbols.py +48 -0
- isar_tools-0.1.0/src/isar_tools/source/theory.py +387 -0
- isar_tools-0.1.0/src/isar_tools/stats/__init__.py +0 -0
- isar_tools-0.1.0/src/isar_tools/stats/build.py +278 -0
- isar_tools-0.1.0/src/isar_tools/stats/cli.py +213 -0
- isar_tools-0.1.0/src/isar_tools/stats/metrics.py +154 -0
- isar_tools-0.1.0/src/isar_tools/stats/views.py +295 -0
- isar_tools-0.1.0/src/isar_tools/style.py +73 -0
- isar_tools-0.1.0/src/isar_tools/symbols_cli.py +61 -0
- isar_tools-0.1.0/tests/cli/test_check_cli.py +114 -0
- isar_tools-0.1.0/tests/cli/test_check_docs_cli.py +45 -0
- isar_tools-0.1.0/tests/cli/test_extract_cli.py +169 -0
- isar_tools-0.1.0/tests/cli/test_fmt_cli.py +100 -0
- isar_tools-0.1.0/tests/cli/test_hierarchy_cli.py +92 -0
- isar_tools-0.1.0/tests/cli/test_project_cli.py +60 -0
- isar_tools-0.1.0/tests/cli/test_stats_build_cli.py +170 -0
- isar_tools-0.1.0/tests/cli/test_stats_cli.py +139 -0
- isar_tools-0.1.0/tests/cli/test_symbols_cli.py +55 -0
- isar_tools-0.1.0/tests/conftest.py +46 -0
- isar_tools-0.1.0/tests/corpus/test_corpus.py +68 -0
- isar_tools-0.1.0/tests/formatter/expected/apply_scripts.thy +21 -0
- isar_tools-0.1.0/tests/formatter/expected/context.thy +18 -0
- isar_tools-0.1.0/tests/formatter/expected/crlf.thy +4 -0
- isar_tools-0.1.0/tests/formatter/expected/layout.thy +28 -0
- isar_tools-0.1.0/tests/formatter/expected/plan_example.thy +18 -0
- isar_tools-0.1.0/tests/formatter/expected/structure.thy +26 -0
- isar_tools-0.1.0/tests/formatter/expected-normalize/apply_scripts.thy +21 -0
- isar_tools-0.1.0/tests/formatter/expected-normalize/context.thy +18 -0
- isar_tools-0.1.0/tests/formatter/expected-normalize/crlf.thy +4 -0
- isar_tools-0.1.0/tests/formatter/expected-normalize/layout.thy +28 -0
- isar_tools-0.1.0/tests/formatter/expected-normalize/plan_example.thy +18 -0
- isar_tools-0.1.0/tests/formatter/expected-normalize/structure.thy +26 -0
- isar_tools-0.1.0/tests/formatter/input/apply_scripts.thy +21 -0
- isar_tools-0.1.0/tests/formatter/input/context.thy +18 -0
- isar_tools-0.1.0/tests/formatter/input/crlf.thy +4 -0
- isar_tools-0.1.0/tests/formatter/input/layout.thy +30 -0
- isar_tools-0.1.0/tests/formatter/input/plan_example.thy +18 -0
- isar_tools-0.1.0/tests/formatter/input/structure.thy +26 -0
- isar_tools-0.1.0/tests/formatter/test_formatter.py +77 -0
- isar_tools-0.1.0/tests/formatter/test_wrap.py +62 -0
- isar_tools-0.1.0/tests/golden/check/a.json +18 -0
- isar_tools-0.1.0/tests/golden/check/default.txt +3 -0
- isar_tools-0.1.0/tests/golden/check/docs.txt +3 -0
- isar_tools-0.1.0/tests/golden/check/locales.txt +1 -0
- isar_tools-0.1.0/tests/golden/extract/kinds.txt +5 -0
- isar_tools-0.1.0/tests/golden/extract/names.json +44 -0
- isar_tools-0.1.0/tests/golden/extract/names.md +17 -0
- isar_tools-0.1.0/tests/golden/extract/names.txt +7 -0
- isar_tools-0.1.0/tests/golden/extract/one.json +12 -0
- isar_tools-0.1.0/tests/golden/extract/print.txt +7 -0
- isar_tools-0.1.0/tests/golden/hierarchy/all.txt +12 -0
- isar_tools-0.1.0/tests/golden/hierarchy/numeric.json +59 -0
- isar_tools-0.1.0/tests/golden/hierarchy/own_only.txt +12 -0
- isar_tools-0.1.0/tests/golden/hierarchy/root.dot +6 -0
- isar_tools-0.1.0/tests/golden/hierarchy/root.txt +7 -0
- isar_tools-0.1.0/tests/golden/hierarchy/sorts.dot +8 -0
- isar_tools-0.1.0/tests/golden/project/graph.dot +10 -0
- isar_tools-0.1.0/tests/golden/project/graph.txt +8 -0
- isar_tools-0.1.0/tests/golden/project/sessions.txt +7 -0
- isar_tools-0.1.0/tests/golden/project/theories.csv +4 -0
- isar_tools-0.1.0/tests/golden/project/theories.dot +9 -0
- isar_tools-0.1.0/tests/golden/project/theories_graph.txt +7 -0
- isar_tools-0.1.0/tests/golden/render/two_tables.csv +9 -0
- isar_tools-0.1.0/tests/golden/render/two_tables.json +22 -0
- isar_tools-0.1.0/tests/golden/render/two_tables.markdown +13 -0
- isar_tools-0.1.0/tests/golden/render/two_tables.text +11 -0
- isar_tools-0.1.0/tests/golden/stats/build.json +53 -0
- isar_tools-0.1.0/tests/golden/stats/build.txt +14 -0
- isar_tools-0.1.0/tests/golden/stats/build_budgets.txt +19 -0
- isar_tools-0.1.0/tests/golden/stats/commands.txt +23 -0
- isar_tools-0.1.0/tests/golden/stats/core.json +34 -0
- isar_tools-0.1.0/tests/golden/stats/proofs.csv +5 -0
- isar_tools-0.1.0/tests/golden/stats/sessions.txt +6 -0
- isar_tools-0.1.0/tests/golden/stats/style.txt +6 -0
- isar_tools-0.1.0/tests/golden/stats/style_default.txt +4 -0
- isar_tools-0.1.0/tests/golden/stats/summary.md +15 -0
- isar_tools-0.1.0/tests/golden/stats/summary.txt +13 -0
- isar_tools-0.1.0/tests/golden/stats/theories_top2.txt +5 -0
- isar_tools-0.1.0/tests/property/test_formatter_properties.py +97 -0
- isar_tools-0.1.0/tests/property/test_lexer_properties.py +70 -0
- isar_tools-0.1.0/tests/unit/test_build.py +141 -0
- isar_tools-0.1.0/tests/unit/test_checks.py +161 -0
- isar_tools-0.1.0/tests/unit/test_cli.py +65 -0
- isar_tools-0.1.0/tests/unit/test_docs_check.py +109 -0
- isar_tools-0.1.0/tests/unit/test_hierarchy.py +126 -0
- isar_tools-0.1.0/tests/unit/test_lexer.py +92 -0
- isar_tools-0.1.0/tests/unit/test_locales.py +150 -0
- isar_tools-0.1.0/tests/unit/test_metrics.py +90 -0
- isar_tools-0.1.0/tests/unit/test_model.py +176 -0
- isar_tools-0.1.0/tests/unit/test_names.py +141 -0
- isar_tools-0.1.0/tests/unit/test_render.py +32 -0
- isar_tools-0.1.0/tests/unit/test_root.py +131 -0
- isar_tools-0.1.0/tests/unit/test_style.py +45 -0
- isar_tools-0.1.0/tests/unit/test_symbols.py +38 -0
- isar_tools-0.1.0/tests/unit/test_theory.py +264 -0
- isar_tools-0.1.0/tests/unit/test_workspace.py +64 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-09-27)
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
- Packaging: the sdist holds sources, tests, and documentation only (no lock files or
|
|
8
|
+
demo recording); README links are absolute, so they work on PyPI.
|
|
9
|
+
`docs/RELEASING.md` describes a release, including the conda-forge recipe.
|
|
10
|
+
- `-d DIR` must name an existing directory (exit status 2 otherwise), as with
|
|
11
|
+
`isabelle build -d`. Before, a mistyped `-d` was ignored, and commands of that
|
|
12
|
+
directory were silently read as part of the previous command.
|
|
13
|
+
- `isar project names`: named declarations with qualified name (`Theory.locale.name`),
|
|
14
|
+
kind, command, session, location, and docstring (a directly preceding `text` block), as
|
|
15
|
+
text, JSON, CSV, or a Markdown index grouped by session and theory. `--kind` filters;
|
|
16
|
+
`--name` (base or exact qualified name) exits 1 for a name that declares nothing and
|
|
17
|
+
suggests the qualified names that exist. Replaces Voblint's
|
|
18
|
+
`extract_definitions.py --dump` and `check_theory_anchors.py`.
|
|
19
|
+
- `isar ... | head` no longer prints a `BrokenPipeError` traceback.
|
|
20
|
+
- `isar project extract NAME...`: the source of a declaration (command and, for a goal,
|
|
21
|
+
its proof), found by `name`, `locale.name`, `Theory.name`, or `Theory.locale.name`; a
|
|
22
|
+
name must identify exactly one declaration. `--manifest TOML --out DIR --write|--check`
|
|
23
|
+
keeps quoted declarations in a document in sync with the theories (the format of
|
|
24
|
+
Voblint's `thesis/shared/snippets.toml`).
|
|
25
|
+
- `isar check docs`: opt-in documentation-coverage group, ported from Voblint's
|
|
26
|
+
`extract_definitions.py --lint`. `undocumented-theory` (no `text` before the first
|
|
27
|
+
declaration), `undocumented-heading` (no `text` right after or before a heading),
|
|
28
|
+
`undocumented-locale` and `undocumented-class` (no `text` right before the declaration).
|
|
29
|
+
`(* *)` comments, formal comments, and `text_raw` do not count as documentation.
|
|
30
|
+
- `isar check locales` (opt-in group, code `locale-free-variable`): identifiers in the
|
|
31
|
+
terms of `locale` and unnamed `context` headers that are no parameter (own, `for`
|
|
32
|
+
clause, `defines`, or inherited from parent locales through imports), not bound in the
|
|
33
|
+
term, and not used anywhere else in the project or its `-d` imports; Isabelle would
|
|
34
|
+
generalize over them. Heuristic filter: at least four characters and an underscore.
|
|
35
|
+
`--allow NAME` (repeatable). Ported from Voblint's `check_locale_parameters.py`.
|
|
36
|
+
- `isar project hierarchy` model: declarations also record `for` clause parameters,
|
|
37
|
+
`defines`, and term tokens; `context` headers parse too; `opening` ends a locale
|
|
38
|
+
expression.
|
|
39
|
+
- Lexer: `\<in>`, `\<le>`, and other two-letter symbols are no longer identifier letters
|
|
40
|
+
(only doubled letters such as `\<AA>` are), and `\<^sup>`/`\<^bold>` no longer continue
|
|
41
|
+
an identifier.
|
|
42
|
+
- `isar project hierarchy`: class and locale declarations (parents, fixes with type and
|
|
43
|
+
notation, assumes, parameter sorts) as text, JSON, or DOT; `--root` follows parents
|
|
44
|
+
through imports, including `-d` directories, and reports unresolved names.
|
|
45
|
+
- Unqualified imports of global theory names (`Main`) resolve through the parent
|
|
46
|
+
session chain.
|
|
47
|
+
- `isar stats build BUILD_LOG`: where theory elaboration time went in an `isabelle build -v`
|
|
48
|
+
log. Per building session: theories elaborated, cpu seconds, and the share spent on
|
|
49
|
+
theories owned by other sessions; theories elaborated more than once with the time
|
|
50
|
+
wasted (`--top N`); `--budget SESSION=N` (repeatable) exits 1 when SESSION's theories
|
|
51
|
+
are elaborated inside other sessions more than N times. Reads only the line format
|
|
52
|
+
`SESSION: theory OWNER.THEORY 100% (Ns cumulated time)`; a log without such lines, or one
|
|
53
|
+
in which a session elaborates a theory twice, is exit status 2 (`--allow-empty` accepts
|
|
54
|
+
an incremental build that rebuilt nothing). `build` is now a view
|
|
55
|
+
name, so a directory called `build` needs `isar stats ./build`.
|
|
56
|
+
- `isar check` prints a summary line (`2 findings: 1 missing-theory, ...`) and colours
|
|
57
|
+
findings on a terminal; `fmt --diff` and `symbols normalize --diff` colour their diffs.
|
|
58
|
+
`--color auto|always|never`; `NO_COLOR` is honoured. Finding messages are shorter.
|
|
59
|
+
- README demo GIF recorded by VHS from `docs/demo/demo.tape` (`pixi run demo`, in a
|
|
60
|
+
separate `demo` environment). It shows `isar fmt --diff`; `pixi run demo-check` runs the
|
|
61
|
+
tape's commands without recording and checks their exit status.
|
|
62
|
+
- Source model: lossless outer-syntax lexer, Isabelle symbol table, theory headers,
|
|
63
|
+
command segmentation with per-theory keyword tables, and goal blocks.
|
|
64
|
+
- Project model: ROOT parser with positioned diagnostics, `ROOTS` discovery, theory and
|
|
65
|
+
import resolution, reachability.
|
|
66
|
+
- `isar stats` with views `summary`, `sessions`, `theories`, `proofs`, `commands`, and
|
|
67
|
+
`style`, in text, Markdown, JSON, and CSV.
|
|
68
|
+
- `isar check` with groups `project`, `proofs`, `syntax` (default) and `symbols`; stable
|
|
69
|
+
finding codes, `--ignore`, text/JSON/CSV output, exit status 1 on findings.
|
|
70
|
+
- `isar symbols normalize` (ASCII or Unicode spelling; `--check`, `--diff`).
|
|
71
|
+
- `isar project sessions|theories|graph` (graph as text, JSON, or DOT).
|
|
72
|
+
- `-d DIR` on `stats`, `check`, and `project`: sessions of DIR resolve imports and
|
|
73
|
+
commands, like `isabelle build -d`.
|
|
74
|
+
- Source files are read and written byte-exactly, so CRLF line endings survive.
|
|
75
|
+
- `isar fmt`: conservative formatter (indentation from proof structure, trailing
|
|
76
|
+
whitespace, blank lines); `--check`, `--diff`, `--normalize`, stdin via `-`. Changes
|
|
77
|
+
only layout, by construction and by test.
|
|
78
|
+
- Opt-in corpus tests (`pixi run corpus` with `ISAR_CORPUS`).
|
|
79
|
+
- Repository scaffold: pixi environment, package skeleton, `isar` CLI with
|
|
80
|
+
placeholder commands, tests, lint, type checking, and CI.
|
isar_tools-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Manuel Lerchner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: isar-tools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Source tooling for Isabelle/Isar projects: formatting, checks, and statistics.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ManuelLerchner/isar-tools
|
|
6
|
+
Project-URL: Repository, https://github.com/ManuelLerchner/isar-tools
|
|
7
|
+
Project-URL: Issues, https://github.com/ManuelLerchner/isar-tools/issues
|
|
8
|
+
Author: Manuel Lerchner
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: formatter,isabelle,isar,theorem-proving
|
|
12
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# isar-tools
|
|
23
|
+
|
|
24
|
+
[![CI Status][ci-badge]][ci]
|
|
25
|
+
[![License][license-badge]][license]
|
|
26
|
+
|
|
27
|
+
[ci-badge]: https://img.shields.io/github/actions/workflow/status/ManuelLerchner/isar-tools/ci.yml?branch=main&style=flat-square&label=CI
|
|
28
|
+
[ci]: https://github.com/ManuelLerchner/isar-tools/actions/workflows/ci.yml
|
|
29
|
+
[license-badge]: https://img.shields.io/github/license/ManuelLerchner/isar-tools?style=flat-square
|
|
30
|
+
[license]: https://github.com/ManuelLerchner/isar-tools/blob/main/LICENSE
|
|
31
|
+
|
|
32
|
+
Source tooling for Isabelle/Isar projects: a formatter, project and source
|
|
33
|
+
checks, and statistics. Pure Python. Works on `.thy` and `ROOT` files without
|
|
34
|
+
running Isabelle.
|
|
35
|
+
|
|
36
|
+

|
|
40
|
+
|
|
41
|
+
Status: pre-alpha. See [`CHANGELOG.md`](https://github.com/ManuelLerchner/isar-tools/blob/main/CHANGELOG.md) and
|
|
42
|
+
[`docs/PLAN.md`](https://github.com/ManuelLerchner/isar-tools/blob/main/docs/PLAN.md).
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
pip install isar-tools # or: uv tool install isar-tools, pipx install isar-tools
|
|
48
|
+
isar --help
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Python 3.11 or newer; no runtime dependencies. A conda-forge package
|
|
52
|
+
(`pixi global install isar-tools`) follows the first PyPI release.
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
| Command | What it does |
|
|
57
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
58
|
+
| `isar fmt [PATH...]` | Format theories: indentation, trailing whitespace, blank lines, and optional line wrapping |
|
|
59
|
+
| `isar check [GROUP] [PATH...]` | Report problems in ROOT files, proofs, syntax, symbols, docs, and locales |
|
|
60
|
+
| `isar stats [VIEW] [PATH...]` | Size, proof, and command statistics |
|
|
61
|
+
| `isar stats build BUILD_LOG` | Where theory elaboration time went in an `isabelle build -v` log |
|
|
62
|
+
| `isar project sessions\|theories\|graph` | Sessions, theories, and the session or theory import graph |
|
|
63
|
+
| `isar project hierarchy` | Class and locale declarations: parents, parameters, assumptions |
|
|
64
|
+
| `isar project names` | Named declarations: qualified name, kind, location, docstring; a Markdown index |
|
|
65
|
+
| `isar project extract NAME...` | The source of a declaration by name; keeps quoted declarations in sync with a manifest |
|
|
66
|
+
| `isar symbols normalize PATH...` | Rewrite symbols as `\<name>`, or as Unicode |
|
|
67
|
+
|
|
68
|
+
A path is a project directory, read like `isabelle build -D` (its `ROOT`, and
|
|
69
|
+
`ROOTS` recursively), or a `.thy` file. Commands that read a project default to the current
|
|
70
|
+
directory.
|
|
71
|
+
|
|
72
|
+
Exit status: `0` success, `1` findings or differences, `2` invalid invocation or
|
|
73
|
+
unreadable input. Data goes to stdout, diagnostics to stderr. JSON and CSV output
|
|
74
|
+
use stable snake_case keys and are never coloured.
|
|
75
|
+
|
|
76
|
+
### Formatting
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
isar fmt # format every .thy below the current directory
|
|
80
|
+
isar fmt --check # list files that would change; exit 1 if any
|
|
81
|
+
isar fmt --diff Foo.thy # show the change without writing
|
|
82
|
+
isar fmt --max-line-length 100 # also wrap long lines
|
|
83
|
+
isar fmt - # stdin to stdout, for editors
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The formatter only changes layout. It never changes a token, never touches the
|
|
87
|
+
space between two tokens on a line, and never rewrites strings, cartouches,
|
|
88
|
+
comments, or ML. By default it only indents lines that are too shallow for their
|
|
89
|
+
proof structure; `--normalize` sets indentation exactly. See
|
|
90
|
+
[`docs/FORMATTER.md`](https://github.com/ManuelLerchner/isar-tools/blob/main/docs/FORMATTER.md).
|
|
91
|
+
|
|
92
|
+
As a git hook with [lefthook](https://github.com/evilmartians/lefthook):
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
pre-commit:
|
|
96
|
+
jobs:
|
|
97
|
+
- name: isar-fmt
|
|
98
|
+
glob: "*.thy"
|
|
99
|
+
run: isar fmt {staged_files}
|
|
100
|
+
stage_fixed: true
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Checks
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
isar check # groups project, proofs, and syntax
|
|
107
|
+
isar check symbols src/ # non-ASCII characters outside comments
|
|
108
|
+
isar check docs src/ # theories, headings, locales, classes without a text block
|
|
109
|
+
isar check locales -d ~/afp/thys # free variables in locale headers
|
|
110
|
+
isar check --ignore oops --format json
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
| Group | Codes |
|
|
114
|
+
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
115
|
+
| `project` | `root-syntax`, `duplicate-session`, `missing-theory`, `missing-directory`, `missing-document-file`, `duplicate-theory-name`, `unreached-theory` |
|
|
116
|
+
| `proofs` | `unfinished-proof` (`sorry`, `\<proof>`), `oops`, `unclosed-proof` |
|
|
117
|
+
| `syntax` | `lexical-error`, `document-argument` |
|
|
118
|
+
| `symbols` | `non-ascii` (opt-in) |
|
|
119
|
+
| `docs` | `undocumented-theory`, `undocumented-heading`, `undocumented-locale`, `undocumented-class` (opt-in) |
|
|
120
|
+
| `locales` | `locale-free-variable` (opt-in, heuristic) |
|
|
121
|
+
|
|
122
|
+
Project checks run for directory arguments only. `isar check --help` describes
|
|
123
|
+
every code.
|
|
124
|
+
|
|
125
|
+
`locales` is heuristic. Inside the terms of a `locale` or `context` header,
|
|
126
|
+
Isabelle reads an unknown identifier as a free variable and generalizes over
|
|
127
|
+
it, so an assumption citing a deleted or misspelt constant still builds. The
|
|
128
|
+
check reports identifiers that are no parameter of the header (its `fixes`,
|
|
129
|
+
`for` clause, `defines`, or those of the locales it extends, resolved through
|
|
130
|
+
imports), not bound in the term, and not used anywhere else in the project or
|
|
131
|
+
in the `-d` theories it imports. Only names of at least four characters with an
|
|
132
|
+
underscore (or `\<^sub>`) are reported; `--allow NAME` (repeatable) accepts a
|
|
133
|
+
name. Inner syntax is approximated lexically: see `isar_tools/checks/locales.py`.
|
|
134
|
+
|
|
135
|
+
### Commands of other sessions
|
|
136
|
+
|
|
137
|
+
Whether a word is an Isar command depends on the theories a file imports. The
|
|
138
|
+
built-in table covers Pure and HOL, and commands declared in the theory headers
|
|
139
|
+
of the project are found automatically. For commands of other projects, such as
|
|
140
|
+
AFP entries, pass their directory with `-d`, as with `isabelle build -d`:
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
isar check -d ~/afp/thys .
|
|
144
|
+
isar project hierarchy --root numeric_domain -d ~/afp/thys --format json
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Names
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
isar project names --kind locale # every locale, as Theory.locale
|
|
151
|
+
isar project names --format markdown > NAMES.md # an index with docstrings
|
|
152
|
+
isar project names --name Foo.loc.bar_lemma # exit 1 if no such declaration
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Names are qualified as Isabelle renders them: a lemma inside `context loc` is
|
|
156
|
+
`Theory.loc.name`. With `--name`, a qualifier naming the wrong scope does not
|
|
157
|
+
match, and the error suggests the names that exist, so links into rendered
|
|
158
|
+
theories can be checked without building them. The docstring is a `text` block
|
|
159
|
+
directly before the declaration.
|
|
160
|
+
|
|
161
|
+
### Quoting declarations
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
isar project extract combine_env locale_name.lemma_name
|
|
165
|
+
isar project extract --manifest snippets.toml --out generated/ --write # regenerate
|
|
166
|
+
isar project extract --manifest snippets.toml --out generated/ --check # diff; exit 1 on drift
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
A name is `name`, `locale.name`, `Theory.name`, or `Theory.locale.name`, and
|
|
170
|
+
must identify one declaration; its source is the command and, for a goal, its
|
|
171
|
+
proof. A manifest lists names as TOML tables, so a document that quotes a
|
|
172
|
+
definition fails its check when the definition changes or is renamed:
|
|
173
|
+
|
|
174
|
+
```toml
|
|
175
|
+
[snippets.combine_env]
|
|
176
|
+
why = "shown in chapter 3" # free text, ignored
|
|
177
|
+
[snippets.succ_pos]
|
|
178
|
+
file = "src/B.thy" # choose between declarations of the same name
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Statistics
|
|
182
|
+
|
|
183
|
+
```sh
|
|
184
|
+
isar stats # sessions, then the largest theories
|
|
185
|
+
isar stats proofs --top 10 # the longest proofs
|
|
186
|
+
isar stats style --max-line-length 100 # long theories, long lines, sorry, watched methods
|
|
187
|
+
isar stats build build.log --budget HOL-Library=0
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Limits
|
|
191
|
+
|
|
192
|
+
- Nothing runs Isabelle, so nothing is type-checked or proved. A formatted file
|
|
193
|
+
is guaranteed to contain the same tokens; building it is the project's CI's
|
|
194
|
+
job.
|
|
195
|
+
- The built-in command table is hand-written. A command of a session that is
|
|
196
|
+
neither built in nor passed with `-d` is read as part of the previous command.
|
|
197
|
+
- `project extract` finds names that commands declare; derived names
|
|
198
|
+
(`foo_def`, `foo.simps`) and names made by interpretations are not found.
|
|
199
|
+
- `project hierarchy` follows declared parents, not `sublocale`, `subclass`, or
|
|
200
|
+
`interpretation`.
|
|
201
|
+
- `stats build` reads one log line format, observed in Isabelle2025 logs.
|
|
202
|
+
|
|
203
|
+
## Development
|
|
204
|
+
|
|
205
|
+
Requires [pixi](https://pixi.sh).
|
|
206
|
+
|
|
207
|
+
```sh
|
|
208
|
+
pixi run pre-commit-install # git hooks
|
|
209
|
+
pixi run test
|
|
210
|
+
pixi run coverage # 100% line and branch coverage required
|
|
211
|
+
pixi run typecheck # pyright, strict
|
|
212
|
+
pixi run lint
|
|
213
|
+
pixi run isar --help
|
|
214
|
+
pixi run demo # re-record docs/demo/demo.gif with VHS
|
|
215
|
+
pixi run demo-check # run the demo commands without recording
|
|
216
|
+
ISAR_CORPUS=~/afp/thys pixi run corpus # integration tests over real projects
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Expected output lives in golden files under `tests/golden/` and
|
|
220
|
+
`tests/formatter/`. `UPDATE_GOLDEN=1 pixi run test` rewrites them, so a change in
|
|
221
|
+
output shows up as a diff in review.
|
|
222
|
+
|
|
223
|
+
The demo GIF is generated by [VHS](https://github.com/charmbracelet/vhs) from
|
|
224
|
+
[`docs/demo/demo.tape`](https://github.com/ManuelLerchner/isar-tools/blob/main/docs/demo/demo.tape), which runs the real commands on the
|
|
225
|
+
small project in [`docs/demo/project`](https://github.com/ManuelLerchner/isar-tools/tree/main/docs/demo/project). The `demo` task uses
|
|
226
|
+
its own pixi environment, so VHS, ttyd, and ffmpeg stay out of the default one.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# isar-tools
|
|
2
|
+
|
|
3
|
+
[![CI Status][ci-badge]][ci]
|
|
4
|
+
[![License][license-badge]][license]
|
|
5
|
+
|
|
6
|
+
[ci-badge]: https://img.shields.io/github/actions/workflow/status/ManuelLerchner/isar-tools/ci.yml?branch=main&style=flat-square&label=CI
|
|
7
|
+
[ci]: https://github.com/ManuelLerchner/isar-tools/actions/workflows/ci.yml
|
|
8
|
+
[license-badge]: https://img.shields.io/github/license/ManuelLerchner/isar-tools?style=flat-square
|
|
9
|
+
[license]: https://github.com/ManuelLerchner/isar-tools/blob/main/LICENSE
|
|
10
|
+
|
|
11
|
+
Source tooling for Isabelle/Isar projects: a formatter, project and source
|
|
12
|
+
checks, and statistics. Pure Python. Works on `.thy` and `ROOT` files without
|
|
13
|
+
running Isabelle.
|
|
14
|
+
|
|
15
|
+

|
|
19
|
+
|
|
20
|
+
Status: pre-alpha. See [`CHANGELOG.md`](https://github.com/ManuelLerchner/isar-tools/blob/main/CHANGELOG.md) and
|
|
21
|
+
[`docs/PLAN.md`](https://github.com/ManuelLerchner/isar-tools/blob/main/docs/PLAN.md).
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pip install isar-tools # or: uv tool install isar-tools, pipx install isar-tools
|
|
27
|
+
isar --help
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Python 3.11 or newer; no runtime dependencies. A conda-forge package
|
|
31
|
+
(`pixi global install isar-tools`) follows the first PyPI release.
|
|
32
|
+
|
|
33
|
+
## Commands
|
|
34
|
+
|
|
35
|
+
| Command | What it does |
|
|
36
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
37
|
+
| `isar fmt [PATH...]` | Format theories: indentation, trailing whitespace, blank lines, and optional line wrapping |
|
|
38
|
+
| `isar check [GROUP] [PATH...]` | Report problems in ROOT files, proofs, syntax, symbols, docs, and locales |
|
|
39
|
+
| `isar stats [VIEW] [PATH...]` | Size, proof, and command statistics |
|
|
40
|
+
| `isar stats build BUILD_LOG` | Where theory elaboration time went in an `isabelle build -v` log |
|
|
41
|
+
| `isar project sessions\|theories\|graph` | Sessions, theories, and the session or theory import graph |
|
|
42
|
+
| `isar project hierarchy` | Class and locale declarations: parents, parameters, assumptions |
|
|
43
|
+
| `isar project names` | Named declarations: qualified name, kind, location, docstring; a Markdown index |
|
|
44
|
+
| `isar project extract NAME...` | The source of a declaration by name; keeps quoted declarations in sync with a manifest |
|
|
45
|
+
| `isar symbols normalize PATH...` | Rewrite symbols as `\<name>`, or as Unicode |
|
|
46
|
+
|
|
47
|
+
A path is a project directory, read like `isabelle build -D` (its `ROOT`, and
|
|
48
|
+
`ROOTS` recursively), or a `.thy` file. Commands that read a project default to the current
|
|
49
|
+
directory.
|
|
50
|
+
|
|
51
|
+
Exit status: `0` success, `1` findings or differences, `2` invalid invocation or
|
|
52
|
+
unreadable input. Data goes to stdout, diagnostics to stderr. JSON and CSV output
|
|
53
|
+
use stable snake_case keys and are never coloured.
|
|
54
|
+
|
|
55
|
+
### Formatting
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
isar fmt # format every .thy below the current directory
|
|
59
|
+
isar fmt --check # list files that would change; exit 1 if any
|
|
60
|
+
isar fmt --diff Foo.thy # show the change without writing
|
|
61
|
+
isar fmt --max-line-length 100 # also wrap long lines
|
|
62
|
+
isar fmt - # stdin to stdout, for editors
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The formatter only changes layout. It never changes a token, never touches the
|
|
66
|
+
space between two tokens on a line, and never rewrites strings, cartouches,
|
|
67
|
+
comments, or ML. By default it only indents lines that are too shallow for their
|
|
68
|
+
proof structure; `--normalize` sets indentation exactly. See
|
|
69
|
+
[`docs/FORMATTER.md`](https://github.com/ManuelLerchner/isar-tools/blob/main/docs/FORMATTER.md).
|
|
70
|
+
|
|
71
|
+
As a git hook with [lefthook](https://github.com/evilmartians/lefthook):
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
pre-commit:
|
|
75
|
+
jobs:
|
|
76
|
+
- name: isar-fmt
|
|
77
|
+
glob: "*.thy"
|
|
78
|
+
run: isar fmt {staged_files}
|
|
79
|
+
stage_fixed: true
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Checks
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
isar check # groups project, proofs, and syntax
|
|
86
|
+
isar check symbols src/ # non-ASCII characters outside comments
|
|
87
|
+
isar check docs src/ # theories, headings, locales, classes without a text block
|
|
88
|
+
isar check locales -d ~/afp/thys # free variables in locale headers
|
|
89
|
+
isar check --ignore oops --format json
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| Group | Codes |
|
|
93
|
+
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| `project` | `root-syntax`, `duplicate-session`, `missing-theory`, `missing-directory`, `missing-document-file`, `duplicate-theory-name`, `unreached-theory` |
|
|
95
|
+
| `proofs` | `unfinished-proof` (`sorry`, `\<proof>`), `oops`, `unclosed-proof` |
|
|
96
|
+
| `syntax` | `lexical-error`, `document-argument` |
|
|
97
|
+
| `symbols` | `non-ascii` (opt-in) |
|
|
98
|
+
| `docs` | `undocumented-theory`, `undocumented-heading`, `undocumented-locale`, `undocumented-class` (opt-in) |
|
|
99
|
+
| `locales` | `locale-free-variable` (opt-in, heuristic) |
|
|
100
|
+
|
|
101
|
+
Project checks run for directory arguments only. `isar check --help` describes
|
|
102
|
+
every code.
|
|
103
|
+
|
|
104
|
+
`locales` is heuristic. Inside the terms of a `locale` or `context` header,
|
|
105
|
+
Isabelle reads an unknown identifier as a free variable and generalizes over
|
|
106
|
+
it, so an assumption citing a deleted or misspelt constant still builds. The
|
|
107
|
+
check reports identifiers that are no parameter of the header (its `fixes`,
|
|
108
|
+
`for` clause, `defines`, or those of the locales it extends, resolved through
|
|
109
|
+
imports), not bound in the term, and not used anywhere else in the project or
|
|
110
|
+
in the `-d` theories it imports. Only names of at least four characters with an
|
|
111
|
+
underscore (or `\<^sub>`) are reported; `--allow NAME` (repeatable) accepts a
|
|
112
|
+
name. Inner syntax is approximated lexically: see `isar_tools/checks/locales.py`.
|
|
113
|
+
|
|
114
|
+
### Commands of other sessions
|
|
115
|
+
|
|
116
|
+
Whether a word is an Isar command depends on the theories a file imports. The
|
|
117
|
+
built-in table covers Pure and HOL, and commands declared in the theory headers
|
|
118
|
+
of the project are found automatically. For commands of other projects, such as
|
|
119
|
+
AFP entries, pass their directory with `-d`, as with `isabelle build -d`:
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
isar check -d ~/afp/thys .
|
|
123
|
+
isar project hierarchy --root numeric_domain -d ~/afp/thys --format json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Names
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
isar project names --kind locale # every locale, as Theory.locale
|
|
130
|
+
isar project names --format markdown > NAMES.md # an index with docstrings
|
|
131
|
+
isar project names --name Foo.loc.bar_lemma # exit 1 if no such declaration
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Names are qualified as Isabelle renders them: a lemma inside `context loc` is
|
|
135
|
+
`Theory.loc.name`. With `--name`, a qualifier naming the wrong scope does not
|
|
136
|
+
match, and the error suggests the names that exist, so links into rendered
|
|
137
|
+
theories can be checked without building them. The docstring is a `text` block
|
|
138
|
+
directly before the declaration.
|
|
139
|
+
|
|
140
|
+
### Quoting declarations
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
isar project extract combine_env locale_name.lemma_name
|
|
144
|
+
isar project extract --manifest snippets.toml --out generated/ --write # regenerate
|
|
145
|
+
isar project extract --manifest snippets.toml --out generated/ --check # diff; exit 1 on drift
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
A name is `name`, `locale.name`, `Theory.name`, or `Theory.locale.name`, and
|
|
149
|
+
must identify one declaration; its source is the command and, for a goal, its
|
|
150
|
+
proof. A manifest lists names as TOML tables, so a document that quotes a
|
|
151
|
+
definition fails its check when the definition changes or is renamed:
|
|
152
|
+
|
|
153
|
+
```toml
|
|
154
|
+
[snippets.combine_env]
|
|
155
|
+
why = "shown in chapter 3" # free text, ignored
|
|
156
|
+
[snippets.succ_pos]
|
|
157
|
+
file = "src/B.thy" # choose between declarations of the same name
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Statistics
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
isar stats # sessions, then the largest theories
|
|
164
|
+
isar stats proofs --top 10 # the longest proofs
|
|
165
|
+
isar stats style --max-line-length 100 # long theories, long lines, sorry, watched methods
|
|
166
|
+
isar stats build build.log --budget HOL-Library=0
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Limits
|
|
170
|
+
|
|
171
|
+
- Nothing runs Isabelle, so nothing is type-checked or proved. A formatted file
|
|
172
|
+
is guaranteed to contain the same tokens; building it is the project's CI's
|
|
173
|
+
job.
|
|
174
|
+
- The built-in command table is hand-written. A command of a session that is
|
|
175
|
+
neither built in nor passed with `-d` is read as part of the previous command.
|
|
176
|
+
- `project extract` finds names that commands declare; derived names
|
|
177
|
+
(`foo_def`, `foo.simps`) and names made by interpretations are not found.
|
|
178
|
+
- `project hierarchy` follows declared parents, not `sublocale`, `subclass`, or
|
|
179
|
+
`interpretation`.
|
|
180
|
+
- `stats build` reads one log line format, observed in Isabelle2025 logs.
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
Requires [pixi](https://pixi.sh).
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
pixi run pre-commit-install # git hooks
|
|
188
|
+
pixi run test
|
|
189
|
+
pixi run coverage # 100% line and branch coverage required
|
|
190
|
+
pixi run typecheck # pyright, strict
|
|
191
|
+
pixi run lint
|
|
192
|
+
pixi run isar --help
|
|
193
|
+
pixi run demo # re-record docs/demo/demo.gif with VHS
|
|
194
|
+
pixi run demo-check # run the demo commands without recording
|
|
195
|
+
ISAR_CORPUS=~/afp/thys pixi run corpus # integration tests over real projects
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Expected output lives in golden files under `tests/golden/` and
|
|
199
|
+
`tests/formatter/`. `UPDATE_GOLDEN=1 pixi run test` rewrites them, so a change in
|
|
200
|
+
output shows up as a diff in review.
|
|
201
|
+
|
|
202
|
+
The demo GIF is generated by [VHS](https://github.com/charmbracelet/vhs) from
|
|
203
|
+
[`docs/demo/demo.tape`](https://github.com/ManuelLerchner/isar-tools/blob/main/docs/demo/demo.tape), which runs the real commands on the
|
|
204
|
+
small project in [`docs/demo/project`](https://github.com/ManuelLerchner/isar-tools/tree/main/docs/demo/project). The `demo` task uses
|
|
205
|
+
its own pixi environment, so VHS, ttyd, and ffmpeg stay out of the default one.
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
MIT
|