Skip to content

Releases: dblock/excalc-rs

v0.3.0

Choose a tag to compare

@dblock dblock released this 26 Sep 21:01
d7971c2

[0.3.0] - 2026-09-26

Added

  • Interactive REPL: running calc/excalc with no expression while stdin is a terminal now starts a read-eval-print loop (variables persist across lines within the session, Tab-completes function names, and shows/advances through argument-name hints inside a call's parentheses), with help for usage, about for version/author/license info, vars to list assigned variables, and exit/quit/Ctrl-D to leave, instead of just printing usage.
  • User-defined functions: name(params) := expr defines a function (e.g. f(x) := x^2 + 1), visible to later statements the same way a variable assignment is, listed alongside variables by the REPL's vars command (showing the actual definition, e.g. f(x) = x ^ 2 + 1, not a placeholder). Functions can call themselves or each other; arguments are evaluated in the caller's scope (proper lexical scoping, no accidental self-shadowing). There's no fixed call-count limit; each nested call checks actual remaining stack space and fails gracefully with function call recursion limit exceeded once it's running low, rather than crashing with a native stack overflow. Function names can't collide with built-ins or pi/e, and definitions are session-only, matching variable persistence.
  • Conditionals: cond ? then : else (ternary operator) and its exact equivalent if(cond, then, else) — both short-circuit, evaluating only the taken branch, which lets self-recursive user-defined functions actually terminate at a computed base case instead of always recursing, e.g. fact(n) := n < 2 ? 1 : n * fact(n - 1).
  • Digit-group separators: number literals may use , or _ as a thousands separator (e.g. 1,234 or 1_234_567); a group must be exactly 3 digits, so short argument lists like sum(1,2) still parse as 2 arguments, not a single grouped number. Whichever separator is used first in an expression is echoed back in the printed result, e.g. 1,000 + 1 prints 1,001.
  • Currency symbols: number literals may be prefixed with $, £, €, or ¥ (e.g. $100), which is purely cosmetic and ignored for math. Whichever currency symbol is used first in an expression is echoed back in the printed result, e.g. $1 + 10 prints $11; math across different currency symbols isn't tracked or rejected.
  • REPL command history now persists across sessions, saved to ~/.excalc_history by default (%USERPROFILE%\.excalc_history on Windows if $HOME isn't set); override the location with --history-file <path> or the EXCALC_HISTORY_FILE environment variable, or disable persistence entirely with --no-history.
  • New general/numeric-analysis functions: nthroot(x, n) (general n-th root) and isqrt(n) (integer square root).
  • Combinatorics extras: stirling1(n, k)/stirling2(n, k) (Stirling numbers of the first/second kind), derangement(n), and bell(n) (Bell numbers).
  • New statistics functions: skewness (population skewness) and kurtosis (population excess kurtosis).
  • Unit conversion functions: c2f/f2c (temperature), km2mi/mi2km (distance), kg2lb/lb2kg (mass), m2ft/ft2m (distance).
  • Probability distribution functions: binomialpdf/binomialcdf (binomial), poissonpdf/poissoncdf (Poisson), normalpdf/normalcdf (normal, reusing the existing erf), and zscore (standard score).
  • Geometry functions: distance/manhattan/dot (n-dimensional, splitting a single argument list in half between two points/vectors) and norm (n-dimensional vector magnitude), plus dimension-free shape formulas triarea (Heron's formula), circlearea, circumference, spherevol, spherearea.
  • New number theory functions: carmichael(n) (Carmichael function), aliquot(n) (sum of proper divisors), amicable?(n), coprime?(a, b), order(a, n) (multiplicative order), jacobi(a, n) (Jacobi symbol).
  • Root-finding functions bisect(expr, var, a, b, tolerance) (bisection method) and secant(expr, var, x0, x1, tolerance) (secant method), sharing the same expression-and-bare-variable calling convention as the numeric-integration functions.
  • New sequence/figurate-number functions: lucas(n) (Lucas numbers, sibling to fib), triangular(n), pentagonal(n), hexagonal(n).
  • Bit manipulation functions: popcount(n), bitlen(n), bitreverse(n, width).
  • New general/rounding functions: roundto(x, n) (round to n decimal places), floordiv(a, b) and mod2(a, b) (floored division/modulo, complementing //mod's truncated-towards-zero convention).
  • Combinatorics functions: factorial(n) (named equivalent of postfix !), perm(n, r), catalan(n), multinomial(n, k1, k2, ...).
  • atan2(y, x): two-argument arctangent, using the sign of both arguments to determine the correct quadrant.
  • excalc/calc read the expression from stdin when no argument is given and stdin is not a TTY, e.g. echo "2 + 2" | calc.
  • --help and usage output now credit the author.
  • Windows MSI installer: renamed the default install directory from excalc to ExCalc; added an optional (on by default) "MCP Server Registration" feature that shells out to copilot mcp add and claude mcp add to register excalc-mcp with GitHub Copilot CLI and Claude Code CLI, if either is found on the machine (src/bin/excalc-mcp-setup.rs, run as a deferred, impersonated, best-effort custom action).
  • New standard math/general functions: log2, cbrt, hypot, sign, clamp, lerp.
  • New statistics functions: median, mode, variance, stddev, percentile, covariance, correlation.
  • New number theory functions: primorial, digitsum, digitalroot, palindrome?, nextprime.
  • Base conversion: hex, oct, bin functions to format a non-negative integer as text, and 0x/0o/0b-prefixed literals to parse hex/octal/binary numbers back into ordinary numeric values. First feature to introduce a non-numeric result type (evaluate_value, returning a Value enum of Number/Text); evaluate remains f64-only for backward compatibility and errors if the result is text.

v0.2.0

Choose a tag to compare

@dblock dblock released this 24 Sep 12:23
b6d0a6d

[0.2.0] - 2026-09-24

Added

  • Homebrew formula (Formula/excalc.rb), installable via brew tap dblock/excalc-rs https://github.com/dblock/excalc-rs && brew install excalc. Builds from source via cargo install. CI audits/installs/tests the formula on every push/PR via .github/workflows/homebrew.yml; RELEASING.md documents updating its url/sha256 on every release.

  • Windows MSI installer, built with cargo-wix from wix/main.wxs (bundles excalc, calc, and excalc-mcp, with an optional PATH entry). .github/workflows/msi.yml builds it on every push/PR; .github/workflows/release.yml rebuilds it and attaches it to each published GitHub release.

  • calc binary alias for excalc (same CLI, shorter name).

  • docs/ reference documentation, per function category, marked ported (implemented) vs. planned (extracted from the original Pascal source as a porting reference).

  • README ### Examples section covering every ported operator and function, verified against the evaluator by tests/readme_examples.rs so it can't silently drift out of sync.

  • CONTRIBUTING.md with build/test/PR instructions, replacing the README's ## Development section.

  • CI: test coverage reported to Coveralls via cargo llvm-cov, plus a CI status and coverage badge in README.

  • Test suite brought to ~99% line coverage: unit tests for every reciprocal/hyperbolic trig function and error path, statistics error paths, evaluator error paths (unknown variable, factorial overflow, log/root domain errors), lexer edge cases (decimals, scientific notation), parser error paths, and a new tests/cli.rs exercising the compiled binary end-to-end. The 3 remaining uncovered lines are tan/sec/sech division-by-zero guards that are mathematically unreachable for any real input.

  • excalc-mcp binary: an MCP server exposing a single evaluate tool over stdio (via rmcp), so AI agents that speak MCP can call the evaluator directly instead of shelling out to the CLI. Built and installed by default via the mcp Cargo feature (opt out with cargo install excalc --no-default-features for a CLI-only install). Covered by tests/mcp.rs, including a test that keeps the README's example expression in sync with the real evaluator. README documents one-liners to register it with GitHub Copilot CLI (copilot mcp add) and Claude Code (claude mcp add), plus a worked example combining several functions in one expression.

  • General / rounding functions: abs, frac, intg (alias of trunc), round (round-half-up), trunc, ceil, floor, random(x) (uniform random number in [0, x], via the rand crate).

  • Number theory functions: gcd, lcm, fib, prime?, moebius, mersenne, perfect, fermat, safeprime, primec, primen, mersennegen, mersgen, genmers, sigma, tau, phi/eind, backed by a deterministic Miller-Rabin primality test (valid for the full u64 range).

  • Comparison and logical/bitwise operators: =, >, < (comparison, returning 1/0), xor, xnor, and (& synonym), nand, or, nor (bitwise, operating on operands truncated to i64), plus not(x), shl(x, y), shr(x, y). Precedence: comparison loosest, then logical-or-family, then logical-and-family, then the existing arithmetic chain. Deviates from the original: = now tests equality instead of assigning a variable, and ? (the original's equality test) was dropped since it's no longer needed.

  • Advanced / special functions: gamma, beta, pochhammer, bth, bman, ellipticE, ellipticF (alias ellipticK), ellipticCE, ellipticCK, dilog, dawson, erf, erfc, si, ssi, ci, chi, fresnelC, fresnelS, fresnelF, fresnelG. Most of these are thin wrappers around a generic numeric-integration primitive in the original engine; this port routes them through the same shared adaptive-quadrature engine used by the general-purpose numeric integration functions below (gamma keeps its own fixed-step Riemann sum instead, to match its pre-existing worked example) — see docs/functions/advanced.md.

  • Financial functions: pv, fv, pmt, npv, rate, cterm, term, sln, syd, ddb, db, and the extended payment-timing-aware variants irate, nper, paymt, fval, pval, ipaymt, ppaymt. Unlike the advanced/special functions, these are all closed-form (or, for irate, a literal secant-method root find) and don't depend on numeric integration.

  • General-purpose numeric integration: named composite quadrature rules trapezoid (aliases trapez/trapezoide), simpson, newton, boole, ordersix (alias ordresix), weddle (each over a whole-number sub-interval count n), plus adaptive quadrature int (alias gauss, refining to a requested tolerance). These are the only functions in the port that take an unevaluated expression and a bare variable as arguments instead of plain numbers, evaluating the expression repeatedly at sample points; see docs/functions/numeric-integration.md. This closes out every function category originally planned for the port.

  • Variables: name := expr assigns to a variable, visible to later statements in the same input, which can now be a ;- or newline-separated sequence of statements (assignments and/or plain expressions); the value of the last statement is the result. Assignment is a statement, not an expression, so it can't be nested or chained (x := y := 5), keeping it unambiguous with ='s equality meaning. pi/e remain reserved and can't be reassigned. Variables don't persist across separate calc invocations or MCP tool calls.

  • Degree/radian support: deg(x)/rad(x) convert between radians and degrees, and sind/cosd/tand/asind/acosd/atand are degree-native variants of the six basic circular trig functions. Deliberately stateless/explicit rather than reviving the original's global CalcMode toggle — see docs/functions/standard-math.md and DESIGN.md#angle-units.

Fixed

  • Removed dead/unreachable Token::Minus arm in parser.rs's parse_primary (unary minus is always handled earlier by parse_unary, so this branch could never execute).
  • The original's Ci/Chi formulas reference an undefined variable G (presumably meant to be the Euler-Mascheroni constant), which the generic variable-lookup mechanism silently defaults to 0, making the original results meaningless. This port hardcodes the actual Euler-Mascheroni constant instead.
  • docs/functions/advanced.md's placeholder example ellipticCK(1) → 5.99158934050168 was wrong; corrected to 1.57079632679490 (pi/2), matching ellipticCE(1) since both reduce to the same integral at that boundary.
  • docs/functions/financial.md's draft named the fixed-declining-balance depreciation function fdb; corrected to db, matching the name actually dispatched in the original engine.

v0.1.0

Choose a tag to compare

@dblock dblock released this 24 Sep 00:53
91f17c5

Added

  • Initial scaffold: lexer, parser, AST, evaluator.
  • Core arithmetic: + - * / mod ^ \ (root) ! (factorial) %, parentheses, unary minus.
  • Standard math functions (radians): sin cos tan asin acos atan sinh cosh tanh asinh acosh atanh sec csc cot asec acsc acot sech csch coth asech acsch acoth sqrt ln log logn.
  • Statistics functions: sum average product min max harmonic binom.
  • CLI binary (excalc "2 + 2 * 3").
  • CI: build/test on Linux, macOS, Windows; cargo fmt and cargo clippy checks.