Skip to content
/ phil Public

Minimal symbolic CLI calculator powered by SymPy for exact arithmetic, calculus, equation solving, and ODEs.

License

Notifications You must be signed in to change notification settings

sacchen/phil

Repository files navigation

phil

A minimal command-line calculator for exact arithmetic, symbolic differentiation, integration, algebraic equation solving, and ordinary differential equations.

Powered by SymPy.

Positioning and Philosophy

  • phil is built to be the first-stop calculator for quick terminal math, homework, and symbolic workflows.
  • It aims to be the fastest practical choice before opening WolframAlpha, Google, Python REPL, or Calculate84.
  • It is not trying to replace Desmos for graphing-first workflows.
  • Priorities: speed, correctness, and discoverability.
  • Input should be forgiving: when phil makes an assumption, it should make that interpretation visible to the user.

Inspiration

Roadmap

  • See ROADMAP.md for planned v0.3.0 and v1.0.0 milestones.

Install

Requires uv.

Install from PyPI (no clone required):

uv tool install philcalc

Then run:

phil

Project links:

Local Development Install

From a local clone:

uv tool install .

60-Second Start

uv tool install philcalc
phil --help
phil '1/3 + 1/6'
phil '10^100000 + 1 - 10^100000'
phil '(1 - 25e^5)e^{-5t} + (25e^5 - 1)t e^{-5t} + t e^{-5t} ln(t)'
phil

Then in REPL, try:

  1. d(x^3 + 2*x, x)
  2. int(sin(x), x)
  3. solve(x^2 - 4, x)
  4. msolve(Matrix([[2,1],[1,3]]), Matrix([1,2]))

Usage

One-shot

phil '<expression>'
phil --format pretty '<expression>'
phil --format json '<expression>'
phil --no-simplify '<expression>'
phil --explain-parse '<expression>'
phil --latex '<expression>'
phil --latex-inline '<expression>'
phil --latex-block '<expression>'
phil --wa '<expression>'
phil --wa --copy-wa '<expression>'
phil --color auto '<expression>'
phil --color always '<expression>'
phil --color never '<expression>'
phil "ode y' = y"
phil "ode y' = y, y(0)=1"
phil "linalg solve A=[[2,1],[1,3]] b=[1,2]"
phil "linalg rref A=[[1,2],[2,4]]"
phil --latex 'dy/dx = y'
phil 'dsolve(Eq(d(y(x), x), y(x)), y(x))'
phil :examples
phil :tutorial
phil :ode
phil :linalg

Interactive

phil
phil> <expression>

REPL commands:

  • :h / :help show strict command reference
  • ? / ?? / ??? progressive feature discovery (quick start, speed shortcuts, advanced demos)
  • :examples show runnable high-signal expression patterns
  • :tutorial / :t / :tour show guided first-run tour
  • :ode show ODE cheat sheet and templates
  • :linalg / :la show linear algebra cheat sheet and templates
  • :next / :repeat / :done control interactive tutorial mode (Enter advances to next step while tutorial is active)
  • :v / :version show current version
  • :update / :check compare current vs latest version and print update command
  • :q / :quit / :x exit

The REPL starts with phil vX.Y.Z REPL [status] (:h help, :t tutorial) on interactive terminals (for example, [latest] or [vX.Y.Z available]). When an update is available, startup prints uv tool upgrade philcalc on the next line. REPL prints targeted hint: messages on common errors. Unknown : commands return a short correction hint. Evaluation errors also include: hint: try WolframAlpha: <url>. Complex expressions also print a WolframAlpha equivalent hint after successful evaluation. REPL sessions also keep ans (last result) and support assignment such as A = Matrix([[1,2],[3,4]]). REPL also accepts inline CLI options, e.g. --latex d(x^2, x) or phil --latex "d(x^2, x)". For readable ODE solving, use ode ... input (example: ode y' = y).

Help

phil --help

Wolfram helper

  • By default, complex expressions print a WolframAlpha equivalent link.
  • Links are printed as full URLs for terminal auto-linking (including iTerm2).
  • Use --wa to always print the link.
  • Use --copy-wa to copy the link to your clipboard when shown.
  • Full URLs are usually clickable directly in modern terminals.

Color diagnostics

  • Use --color auto|always|never to control ANSI color on diagnostic lines (E: and hint:).
  • Default is --color auto (enabled only on TTY stderr, disabled for pipes/non-interactive output).
  • NO_COLOR disables auto color.
  • --color always forces color even when output is not a TTY.

Interop Output

  • --format json prints a compact JSON object with input, parsed, and result.
  • --format json keeps diagnostics on stderr, so stdout remains machine-readable.

Clear Input/Output Mode

  • Use --format pretty for easier-to-scan rendered output.
  • Use --explain-parse to print hint: parsed as: ... on stderr before evaluation.
  • Combine with relaxed parsing for shorthand visibility, e.g. phil --explain-parse 'sinx'.
  • stdout stays result-only, so pipes/scripts remain predictable.

Updates

From published package (anywhere):

uv tool upgrade philcalc

From a local clone of this repo:

uv tool install --force --reinstall --refresh .

Quick check in CLI:

phil :version
phil :update
phil :check

In REPL:

  • Startup (interactive terminals) prints a one-line up-to-date or update-available status.
  • :version shows your installed version.
  • :update/:check show current version, latest known release, and update command.
  • ?, ??, ??? progressively reveal shortcuts and capability demos.

For release notifications on GitHub, use "Watch" -> "Custom" -> "Releases only" on the repo page.

Release

Tagged releases are published to PyPI automatically via GitHub Actions trusted publishing. Draft GitHub Release notes live under release-notes/ and should be finalized at tag time. Use scripts/release_notes.sh <version> --body to print copy/paste-ready GitHub Release text.

git pull
git tag -a v0.2.0 -m "Release v0.2.0"
git push origin v0.2.0
# or
scripts/release.sh 0.2.0

Then verify:

Long Expressions (easier input)

phil now uses relaxed parsing by default:

  • 2x works like 2*x
  • sinx works like sin(x) (with a hint: notice)
  • {} works like ()
  • ln(t) works like log(t)

So inputs like these work directly:

phil '(1 - 25e^5)e^{-5t} + (25e^5 - 1)t e^{-5t} + t e^{-5t} ln(t)'
phil '(854/2197)e^{8t}+(1343/2197)e^{-5t}+((9/26)t^2 -(9/169)t)e^{8t}'
phil 'dy/dx = y'

Use strict parsing if needed:

phil --strict '2*x'

Reliability and Recovery

phil is optimized to recover quickly on pathological input while keeping exact math behavior where possible.

  • Cancellable huge expressions stay fast and exact:
    • 10^10000000000 + 1 - 10^10000000000 -> 1
    • 2^(2^20) + 1 - 2^(2^20) -> 1
  • Non-cancellable growth fails fast with local recovery hints:
    • 10^10000000000 + 1
    • 2^(2^(2^20))
    • 100001!
    • factorial(10^10)
  • Ambiguous high-risk shorthand is rejected with explicit guidance:
    • sin x^2 -> use sin(x^2) or (sin(x))^2

Precedence note:

  • -2^2 is interpreted as -(2^2).
  • Use (-2)^2 if you want the negative base squared.

Examples

$ phil '1/3 + 1/6'
1/2

$ phil 'd(x^3 + 2*x, x)'
3*x**2 + 2

$ phil 'int(sin(x), x)'
-cos(x)

$ phil 'solve(x^2 - 4, x)'
[-2, 2]

$ phil 'N(pi, 30)'
3.14159265358979323846264338328

$ phil --latex 'd(x^2, x)'
2 x

$ phil --latex-inline 'd(x^2, x)'
$2 x$

$ phil --latex-block 'd(x^2, x)'
$$
2 x
$$

$ phil --format pretty 'Matrix([[1,2],[3,4]])'
[1  2]
[3  4]

Test

uv run --group dev pytest
# quick local loop (skip process-heavy integration tests)
uv run --group dev pytest -m "not integration"
# full local quality gate
scripts/checks.sh

GitHub

  • CI: .github/workflows/ci.yml runs tests on pushes and PRs.
  • License: MIT (LICENSE).
  • Ignore rules: Python/venv/cache (.gitignore).
  • Contribution guide: CONTRIBUTOR.md.

Learn by Doing

Try this sequence in REPL mode:

  1. 1/3 + 1/6
  2. d(x^3 + 2*x, x)
  3. int(sin(x), x)
  4. solve(x^2 - 4, x)
  5. N(pi, 20)

If you get stuck, run :examples or :h.

Reference

Operations

Operation Syntax
Derivative d(expr, var)
Integral int(expr, var)
Solve equation solve(expr, var)
Solve ODE dsolve(Eq(...), func)
Equation Eq(lhs, rhs)
Numeric eval N(expr, digits)
Integer GCD/LCM gcd(a, b), lcm(a, b)
Primality / factorization isprime(n), factorint(n)
Rational parts num(expr), den(expr)
Matrix determinant det(Matrix([[...]]))
Matrix inverse inv(Matrix([[...]]))
Matrix rank rank(Matrix([[...]]))
Matrix eigenvalues eigvals(Matrix([[...]]))
Matrix RREF rref(Matrix([[...]]))
Matrix nullspace nullspace(Matrix([[...]]))
Solve linear system (Ax=b) msolve(Matrix([[...]]), Matrix([...]))
Symbolic linear solve linsolve((Eq(...), Eq(...)), (x, y))

Symbols

x, y, z, t, pi, e, f

Functions

sin, cos, tan, exp, log, sqrt, abs

Exact arithmetic helpers

gcd, lcm, isprime, factorint, num, den

Symbol helpers

  • symbols("A B C") returns a tuple of symbols.
  • S("A") is shorthand for Symbol("A").

Matrix helpers

Matrix, eye, zeros, ones, det, inv, rank, eigvals, rref, nullspace, msolve, linsolve

Syntax notes

  • ^ is exponentiation (x^2)
  • function exponent notation is accepted (sin^2(x), cos^2(x))
  • ! is factorial (5!)
  • relaxed mode (default) allows implicit multiplication (2x); use --strict to require 2*x
  • d(expr) / int(expr) infer the variable when exactly one symbol is present
  • Leibniz shorthand is accepted: d(sin(x))/dx, df(t)/dt
  • ODE shorthand is accepted: dy/dx = y, y' = y, y'' + y = 0, y'(0)=0
  • LaTeX-style ODE shorthand is accepted: \frac{dy}{dx} = y, \frac{d^2y}{dx^2} + y = 0
  • In ODE input, prefer explicit multiplication (20*y instead of 20y) for predictable parsing.
  • Common LaTeX wrappers and commands are normalized: $...$, \(...\), \sin, \cos, \ln, \sqrt{...}, \frac{a}{b}
  • name = expr assigns in REPL session (ans is always last result)
  • Undefined symbols raise an error

Safety limits

  • Expressions longer than 2000 chars are rejected.
  • Inputs containing blocked tokens like __, ;, or newlines are rejected.

See DESIGN.md for implementation details.

About

Minimal symbolic CLI calculator powered by SymPy for exact arithmetic, calculus, equation solving, and ODEs.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •