philcalc 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.
@@ -0,0 +1,32 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ python-version: ["3.12", "3.13"]
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v6
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - run: uv run --group dev pytest --cov=calc --cov-report=term-missing --cov-fail-under=90
21
+
22
+ smoke-install:
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+ - uses: astral-sh/setup-uv@v6
27
+ - uses: actions/setup-python@v5
28
+ with:
29
+ python-version: "3.12"
30
+ - run: uv tool install --force --reinstall --refresh .
31
+ - run: phil ':version'
32
+ - run: phil 'd(sin(x))/dx'
@@ -0,0 +1,5 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ .pytest_cache/
5
+ .DS_Store
@@ -0,0 +1,57 @@
1
+ # AGENTS
2
+
3
+ Guidance for human/AI contributors working in this repository.
4
+
5
+ ## Mission
6
+
7
+ Keep `phil` accurate, safe, and fast to use for real math workflows in a terminal.
8
+
9
+ ## Non-Negotiables
10
+
11
+ - Do not weaken parser safety (`GLOBAL_DICT`, blocked tokens, input limits).
12
+ - Keep one-shot and REPL semantics aligned.
13
+ - Keep output predictable for piping/scripts.
14
+ - Add tests for every user-visible behavior change.
15
+
16
+ ## Preferred Workflow
17
+
18
+ 1. Implement smallest coherent change.
19
+ 2. Add/adjust tests (`unit`, `integration`, `regression`).
20
+ 3. Update docs (`README.md`, `DESIGN.md`, `KNOWLEDGE.md`, `CONTRIBUTOR.md`) if needed.
21
+ 4. Run:
22
+ - `uv run --group dev pytest`
23
+ - `uv run --group dev pytest --cov=calc --cov-report=term-missing --cov-fail-under=90`
24
+
25
+ ## Perfect Commit Protocol
26
+
27
+ Use "perfect commit" discipline for every change:
28
+
29
+ 1. One logical change per commit.
30
+ 2. Commit must build, test, and run in isolation.
31
+ 3. Include tests for behavior changes or bug fixes.
32
+ 4. Include docs updates for user-visible changes.
33
+ 5. Keep unrelated refactors out of the same commit.
34
+
35
+ Commit message format:
36
+
37
+ - Subject: imperative, specific, <=72 chars.
38
+ - Body: why this change exists, what changed, and user impact.
39
+ - Footer (optional): issue/PR references.
40
+
41
+ Suggested pre-commit gate:
42
+
43
+ - `git diff --staged` is coherent and minimal.
44
+ - tests pass locally for touched areas (or full suite for broad changes).
45
+
46
+ ## High-Value Areas
47
+
48
+ - Parser normalization and syntax sugar (`df/dt`, relaxed input).
49
+ - Error hint quality (`E:` + actionable `hint:`).
50
+ - Matrix/ODE workflow ergonomics.
51
+ - Formatting modes and copy/paste behavior.
52
+
53
+ ## Avoid
54
+
55
+ - Hidden behavior that changes math semantics silently.
56
+ - Implicit defaults when ambiguity exists (prefer explicit error + hint).
57
+ - Breaking existing CLI flags without compatibility path.
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - 2026-02-20
4
+
5
+ - Initial public release.
6
+ - Symbolic CLI calculator with exact arithmetic and core SymPy operations.
7
+ - Hardened parser configuration and input guardrails.
8
+ - Minimal terminal UX (`:h`, `:examples`, `:q`, `:x`) with actionable hints.
9
+ - Optional WolframAlpha fallback hint on evaluation errors.
10
+ - uv package layout, tests, and GitHub Actions CI.
@@ -0,0 +1,90 @@
1
+ # Contributor Guide
2
+
3
+ This project is a terminal-first symbolic calculator (`phil`) built on SymPy.
4
+ Changes should improve correctness, safety, and practical UX for math workflows.
5
+
6
+ ## Development Setup
7
+
8
+ ```bash
9
+ uv run --group dev pytest
10
+ uv run phil '2+2'
11
+ uv run phil
12
+ ```
13
+
14
+ ## Testing Strategy (Required)
15
+
16
+ Run these before pushing:
17
+
18
+ ```bash
19
+ uv run --group dev pytest
20
+ uv run --group dev pytest --cov=calc --cov-report=term-missing --cov-fail-under=90
21
+ ```
22
+
23
+ Test categories:
24
+
25
+ - `unit`: pure behavior of parser/evaluator/format helpers.
26
+ - `integration`: process-level CLI/REPL behavior.
27
+ - `regression`: fixed bug cases that must never regress.
28
+
29
+ Property tests (`hypothesis`) are used for high-value invariants in numeric/symbolic behavior.
30
+
31
+ ## Perfect Commit Standard
32
+
33
+ Adopt a "perfect commit" bar:
34
+
35
+ 1. One logical change per commit.
36
+ 2. Commit is releasable on its own (no broken intermediate states).
37
+ 3. Tests accompany behavior changes.
38
+ 4. Docs accompany user-facing changes.
39
+ 5. Exclude unrelated cleanup from the same commit.
40
+
41
+ Before committing:
42
+
43
+ - Review staged diff only: `git diff --staged`
44
+ - Verify tests:
45
+ - `uv run --group dev pytest`
46
+ - `uv run --group dev pytest --cov=calc --cov-report=term-missing --cov-fail-under=90`
47
+
48
+ Commit message guidance:
49
+
50
+ - Subject: imperative and specific, <=72 chars.
51
+ - Body: explain why, summarize what changed, note user impact.
52
+
53
+ ## CI Expectations
54
+
55
+ CI runs:
56
+
57
+ - tests + coverage on Python `3.12` and `3.13`
58
+ - install smoke test (`uv tool install .`, then run `phil`)
59
+
60
+ If your change adds behavior, add/adjust tests in the correct category.
61
+
62
+ ## Adding or Changing Math Operations
63
+
64
+ 1. Add required import(s) in `src/calc/core.py`.
65
+ 2. Expose user-facing entry in `LOCALS_DICT`.
66
+ 3. Add tests in `tests/test_core.py` (and regression tests if bug-fix related).
67
+ 4. Update `README.md` and `KNOWLEDGE.md` if user-visible.
68
+ 5. Run full suite.
69
+
70
+ ## Safety Rules
71
+
72
+ - Do not loosen parser globals in `GLOBAL_DICT`.
73
+ - Keep blocked-token and input-size protections unless there is a measured reason.
74
+ - Never execute user input outside SymPy parse/eval path.
75
+
76
+ ## UX Rules
77
+
78
+ - Output remains terminal-first and script-friendly.
79
+ - Errors are concise: `E:` + actionable `hint:`.
80
+ - Keep REPL behavior consistent with one-shot mode (`--strict`, `--format`, `--no-simplify`).
81
+
82
+ ## Scope Guardrails
83
+
84
+ Avoid unnecessary complexity:
85
+
86
+ - plugin systems
87
+ - unrelated persistence layers
88
+ - large configuration frameworks
89
+
90
+ When adding complexity, explain the user value and testing impact in the PR.
@@ -0,0 +1,82 @@
1
+ # Design & Implementation
2
+
3
+ ## Architecture
4
+
5
+ ```
6
+ user input
7
+
8
+ parse_expr() ← controlled namespace + restricted globals
9
+
10
+ SymPy expression
11
+
12
+ simplify() ← skipped for list/tuple/dict results
13
+
14
+ print()
15
+ ```
16
+
17
+ ## Project layout
18
+
19
+ ```
20
+ pyproject.toml
21
+ src/calc/core.py ← parser and evaluator
22
+ src/calc/cli.py ← command-line interface
23
+ tests/test_core.py
24
+ tests/test_cli.py
25
+ phil ← local launcher script (uv run --project)
26
+ CONTRIBUTOR.md ← contribution and extension guide
27
+ ```
28
+
29
+ ## Packaging model
30
+
31
+ This project is a standard `uv` package:
32
+
33
+ - `pyproject.toml` declares metadata and dependencies.
34
+ - `[project.scripts]` exposes the `phil` command.
35
+ - Tests run via `uv run --group dev pytest`.
36
+
37
+ ## Parsing model
38
+
39
+ `parse_expr` is configured with:
40
+
41
+ - Controlled `local_dict` (only the symbols and operations we allow)
42
+ - Restricted `global_dict` with `__builtins__` removed
43
+ - Transformations: `auto_number`, `factorial_notation`, `convert_xor`
44
+ - Input validation before parsing (length and blocked-token checks)
45
+ - Normalization pass for user-friendly syntax (`{}` -> `()`, `ln(` -> `log(`)
46
+
47
+ This significantly reduces parser attack surface for CLI usage. It is still not a hardened sandbox for arbitrary untrusted multi-tenant input.
48
+
49
+ By default, CLI evaluation uses relaxed parsing (`implicit_multiplication_application`) to make long calculator-style expressions easier to enter.
50
+ `--strict` disables relaxed parsing in both one-shot and REPL modes.
51
+ `--no-simplify` skips the simplify step for large or structure-sensitive expressions.
52
+
53
+ `d(expr)` and `int(expr)` can infer the variable only when the expression has exactly one free symbol. Ambiguous or symbol-free expressions must pass the variable explicitly.
54
+ REPL evaluation supports session locals, assignment (`name = expr`), and `ans` for last result.
55
+ Matrix helpers are exposed in the allowed namespace (`Matrix`, `eye`, `zeros`, `ones`, `det`, `inv`, `rank`, `eigvals`).
56
+
57
+ ## Exit-code behavior
58
+
59
+ - One-shot mode returns `0` on success.
60
+ - One-shot mode returns `1` on parse/evaluation errors.
61
+ - REPL mode keeps running on expression errors and exits `0` on Ctrl-C/Ctrl-D.
62
+
63
+ ## REPL UX
64
+
65
+ - Prompt is `phil>`.
66
+ - Minimal command mode inspired by terminal-first tools:
67
+ - `:h` or `:help` shows available commands
68
+ - `:examples` shows a compact learning set
69
+ - `:version` shows installed version
70
+ - `:update` / `:check` compare current vs latest version and print upgrade command
71
+ - `:q` or `:quit` exits
72
+ - Errors are terse and prefixed with `E:`.
73
+ - Common failures include contextual `hint:` lines.
74
+ - Evaluation failures include a WolframAlpha URL hint for optional browser lookup.
75
+ - Complex successful expressions also show a WolframAlpha equivalent hint.
76
+ - `--wa` forces hints for all expressions; `--copy-wa` attempts clipboard copy.
77
+ - Optional LaTeX output via `--latex`, `--latex-inline`, or `--latex-block`.
78
+ - Optional output formats via `--format` (`plain`, `pretty`, `latex`, `latex-inline`, `latex-block`).
79
+
80
+ ## Startup time
81
+
82
+ Startup cost is mostly from Python + SymPy import time. Packaging improves distribution/testing and reproducibility, but not raw import latency by itself.
@@ -0,0 +1,56 @@
1
+ # KNOWLEDGE
2
+
3
+ Project-specific knowledge for contributors.
4
+
5
+ ## Product Surface
6
+
7
+ - Distribution name: `philcalc`
8
+ - CLI command: `phil`
9
+ - Core module path: `src/calc/`
10
+
11
+ ## Parsing and Evaluation Model
12
+
13
+ - Uses SymPy `parse_expr` with restricted globals.
14
+ - Default mode is relaxed parsing for calculator-style input.
15
+ - Strict mode (`--strict`) disables relaxed transforms.
16
+ - Optional no-simplify mode (`--no-simplify`) returns parsed form without `simplify()`.
17
+
18
+ ## Supported Syntax Highlights
19
+
20
+ - Derivative:
21
+ - `d(expr, var)`
22
+ - `d(expr)` when variable can be inferred uniquely
23
+ - Leibniz shorthand: `d(sin(x))/dx`, `df(t)/dt`
24
+ - Integral:
25
+ - `int(expr, var)`
26
+ - `int(expr)` with unique variable inference
27
+ - Matrix helpers:
28
+ - `Matrix`, `eye`, `zeros`, `ones`
29
+ - `det`, `inv`, `rank`, `eigvals`
30
+ - Session behavior (REPL):
31
+ - assignment: `A = ...`
32
+ - `ans` = last result
33
+
34
+ ## Formatting Modes
35
+
36
+ - `--format plain` (default)
37
+ - `--format pretty`
38
+ - `--format latex`
39
+ - `--format latex-inline`
40
+ - `--format latex-block`
41
+
42
+ Legacy aliases:
43
+
44
+ - `--latex`, `--latex-inline`, `--latex-block`
45
+
46
+ ## Error UX
47
+
48
+ - Errors start with `E:`
49
+ - Follow-up guidance via `hint:`
50
+ - Syntax-specific hints exist for common derivative and matrix mistakes.
51
+
52
+ ## Testing Expectations
53
+
54
+ - Unit + integration + regression tests.
55
+ - Property tests via `hypothesis` for core invariants.
56
+ - CI checks multiple Python versions and install smoke tests.
philcalc-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Samuel Chen
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,252 @@
1
+ Metadata-Version: 2.4
2
+ Name: philcalc
3
+ Version: 0.1.0
4
+ Summary: Minimal symbolic CLI calculator powered by SymPy
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: sympy>=1.12
8
+ Description-Content-Type: text/markdown
9
+
10
+ # phil
11
+
12
+ A minimal command-line calculator for exact arithmetic, symbolic differentiation, integration, algebraic equation solving, and ordinary differential equations.
13
+
14
+ Powered by [SymPy](https://www.sympy.org/).
15
+
16
+ ## Install
17
+
18
+ Requires [uv](https://docs.astral.sh/uv/).
19
+
20
+ Install from the project directory:
21
+
22
+ ```bash
23
+ uv tool install .
24
+ ```
25
+
26
+ Run without installing:
27
+
28
+ ```bash
29
+ uv run phil '2+2'
30
+ ```
31
+
32
+ ## 60-Second Start
33
+
34
+ ```bash
35
+ uv tool install .
36
+ phil --help
37
+ phil '1/3 + 1/6'
38
+ phil '(1 - 25e^5)e^{-5t} + (25e^5 - 1)t e^{-5t} + t e^{-5t} ln(t)'
39
+ phil
40
+ ```
41
+
42
+ Then in REPL, try:
43
+
44
+ 1. `d(x^3 + 2*x, x)`
45
+ 2. `int(sin(x), x)`
46
+ 3. `solve(x^2 - 4, x)`
47
+
48
+ ## Usage
49
+
50
+ ### One-shot
51
+
52
+ ```bash
53
+ phil '<expression>'
54
+ phil --format pretty '<expression>'
55
+ phil --no-simplify '<expression>'
56
+ phil --latex '<expression>'
57
+ phil --latex-inline '<expression>'
58
+ phil --latex-block '<expression>'
59
+ phil --wa '<expression>'
60
+ phil --wa --copy-wa '<expression>'
61
+ phil :examples
62
+ ```
63
+
64
+ ### Interactive
65
+
66
+ ```bash
67
+ phil
68
+ phil> <expression>
69
+ ```
70
+
71
+ REPL commands:
72
+
73
+ - `:h` / `:help` show help
74
+ - `:examples` show sample expressions
75
+ - `:v` / `:version` show current version
76
+ - `:update` / `:check` compare current vs latest version and print update command
77
+ - `:q` / `:quit` / `:x` exit
78
+
79
+ The REPL starts with a short hint line and prints targeted `hint:` messages on common errors.
80
+ Unknown `:` commands return a short correction hint.
81
+ Evaluation errors also include: `hint: try WolframAlpha: <url>`.
82
+ Complex expressions also print a WolframAlpha equivalent hint after successful evaluation.
83
+ REPL sessions also keep `ans` (last result) and support assignment such as `A = Matrix([[1,2],[3,4]])`.
84
+
85
+ ### Help
86
+
87
+ ```bash
88
+ phil --help
89
+ ```
90
+
91
+ ### Wolfram helper
92
+
93
+ - By default, complex expressions print a WolframAlpha equivalent link.
94
+ - Links are printed as full URLs for terminal auto-linking (including iTerm2).
95
+ - Use `--wa` to always print the link.
96
+ - Use `--copy-wa` to copy the link to your clipboard when shown.
97
+ - Full URLs are usually clickable directly in modern terminals.
98
+
99
+ ## Updates
100
+
101
+ From published package (anywhere):
102
+
103
+ ```bash
104
+ uv tool upgrade philcalc
105
+ ```
106
+
107
+ From a local clone of this repo:
108
+
109
+ ```bash
110
+ uv tool install --force --reinstall --refresh .
111
+ ```
112
+
113
+ Quick check in CLI:
114
+
115
+ ```bash
116
+ phil :version
117
+ phil :update
118
+ phil :check
119
+ ```
120
+
121
+ In REPL:
122
+
123
+ - `:version` shows your installed version.
124
+ - `:update`/`:check` show current version, latest known release, and update command.
125
+
126
+ For release notifications on GitHub, use "Watch" -> "Custom" -> "Releases only" on the repo page.
127
+
128
+ ### Long Expressions (easier input)
129
+
130
+ `phil` now uses relaxed parsing by default:
131
+
132
+ - `2x` works like `2*x`
133
+ - `{}` works like `()`
134
+ - `ln(t)` works like `log(t)`
135
+
136
+ So inputs like these work directly:
137
+
138
+ ```bash
139
+ phil '(1 - 25e^5)e^{-5t} + (25e^5 - 1)t e^{-5t} + t e^{-5t} ln(t)'
140
+ phil '(854/2197)e^{8t}+(1343/2197)e^{-5t}+((9/26)t^2 -(9/169)t)e^{8t}'
141
+ ```
142
+
143
+ Use strict parsing if needed:
144
+
145
+ ```bash
146
+ phil --strict '2*x'
147
+ ```
148
+
149
+ ## Examples
150
+
151
+ ```bash
152
+ $ phil '1/3 + 1/6'
153
+ 1/2
154
+
155
+ $ phil 'd(x^3 + 2*x, x)'
156
+ 3*x**2 + 2
157
+
158
+ $ phil 'int(sin(x), x)'
159
+ -cos(x)
160
+
161
+ $ phil 'solve(x^2 - 4, x)'
162
+ [-2, 2]
163
+
164
+ $ phil 'N(pi, 30)'
165
+ 3.14159265358979323846264338328
166
+
167
+ $ phil --latex 'd(x^2, x)'
168
+ 2 x
169
+
170
+ $ phil --latex-inline 'd(x^2, x)'
171
+ $2 x$
172
+
173
+ $ phil --latex-block 'd(x^2, x)'
174
+ $$
175
+ 2 x
176
+ $$
177
+
178
+ $ phil --format pretty 'Matrix([[1,2],[3,4]])'
179
+ [1 2]
180
+ [3 4]
181
+ ```
182
+
183
+ ## Test
184
+
185
+ ```bash
186
+ uv run --group dev pytest
187
+ ```
188
+
189
+ ## GitHub
190
+
191
+ - CI: `.github/workflows/ci.yml` runs tests on pushes and PRs.
192
+ - License: MIT (`LICENSE`).
193
+ - Ignore rules: Python/venv/cache (`.gitignore`).
194
+ - Contribution guide: `CONTRIBUTOR.md`.
195
+
196
+ ## Learn by Doing
197
+
198
+ Try this sequence in REPL mode:
199
+
200
+ 1. `1/3 + 1/6`
201
+ 2. `d(x^3 + 2*x, x)`
202
+ 3. `int(sin(x), x)`
203
+ 4. `solve(x^2 - 4, x)`
204
+ 5. `N(pi, 20)`
205
+
206
+ If you get stuck, run `:examples` or `:h`.
207
+
208
+ ## Reference
209
+
210
+ ### Operations
211
+
212
+ | Operation | Syntax |
213
+ |-----------|--------|
214
+ | Derivative | `d(expr, var)` |
215
+ | Integral | `int(expr, var)` |
216
+ | Solve equation | `solve(expr, var)` |
217
+ | Solve ODE | `dsolve(Eq(...), func)` |
218
+ | Equation | `Eq(lhs, rhs)` |
219
+ | Numeric eval | `N(expr, digits)` |
220
+ | Matrix determinant | `det(Matrix([[...]]))` |
221
+ | Matrix inverse | `inv(Matrix([[...]]))` |
222
+ | Matrix rank | `rank(Matrix([[...]]))` |
223
+ | Matrix eigenvalues | `eigvals(Matrix([[...]]))` |
224
+
225
+ ### Symbols
226
+
227
+ `x`, `y`, `z`, `t`, `pi`, `e`, `f`
228
+
229
+ ### Functions
230
+
231
+ `sin`, `cos`, `tan`, `exp`, `log`, `sqrt`, `abs`
232
+
233
+ ### Matrix helpers
234
+
235
+ `Matrix`, `eye`, `zeros`, `ones`, `det`, `inv`, `rank`, `eigvals`
236
+
237
+ ### Syntax notes
238
+
239
+ - `^` is exponentiation (`x^2`)
240
+ - `!` is factorial (`5!`)
241
+ - relaxed mode (default) allows implicit multiplication (`2x`); use `--strict` to require `2*x`
242
+ - `d(expr)` / `int(expr)` infer the variable when exactly one symbol is present
243
+ - Leibniz shorthand is accepted: `d(sin(x))/dx`, `df(t)/dt`
244
+ - `name = expr` assigns in REPL session (`ans` is always last result)
245
+ - Undefined symbols raise an error
246
+
247
+ ## Safety limits
248
+
249
+ - Expressions longer than 2000 chars are rejected.
250
+ - Inputs containing blocked tokens like `__`, `;`, or newlines are rejected.
251
+
252
+ See [DESIGN.md](DESIGN.md) for implementation details.