# ccalc
A command-line calculator with a persistent accumulator, memory cells, and math functions.
**Current version: 0.7.0** — see [CHANGELOG](CHANGELOG.md) for history.
---
## Installation
```bash
git clone https://github.com/holgertkey/ccalc
cd ccalc
cargo build --release
```
The binary is placed at `target/release/ccalc`. Copy it anywhere on your `PATH`.
---
## Usage
```
ccalc [OPTIONS] start interactive REPL
ccalc "EXPR" evaluate expression and print result
```
| `-h`, `--help` | Show help |
| `-v`, `--version` | Show version |
---
## Modes
### Interactive REPL
Run without arguments:
```
$ ccalc
[ 0 ]:
```
### Single expression
Pass an expression as a command-line argument:
```
$ ccalc "2 ^ 32"
4294967296
$ ccalc "sqrt(144)"
12
```
### Pipe / non-interactive mode
When stdin is not a terminal, ccalc runs silently — no prompt, one result per line. The accumulator carries over across lines, so you can chain calculations:
```
$ printf "10\n+ 5\n* 2" | ccalc
10
15
30
$ ccalc < formulas.txt
```
All commands work in pipe mode: `q` stops processing, `c` resets the accumulator, `mc`/`mc[1-9]` clear memory, `m[1-9]` stores into a cell, `p`/`p<N>` set precision, `hex`/`dec`/`bin`/`oct`/`base` control number base. `cls` and `m` are ignored.
---
## How it works
The prompt shows the **accumulator** — the result of the last expression. Every new expression updates it. Expressions that start with an operator automatically use the accumulator as the left-hand operand (**partial expressions**):
```
[ 0 ]: 100
[ 100 ]: / 4
[ 25 ]: + 5
[ 30 ]: ^ 2
[ 900 ]:
```
---
## Arithmetic
### Operators
| `^` | Power (right-associative) | highest |
| `*` `/` `%` | Multiply, divide, modulo | medium |
| `+` `-` | Add, subtract | lowest |
```
[ 0 ]: 2 + 3 * 4
[ 14 ]:
[ 0 ]: 2 ^ 3 ^ 2
[ 512 ]: (right-associative: 2^(3^2) = 2^9)
[ 0 ]: 17 % 5
[ 2 ]:
```
### Grouping
```
[ 0 ]: (2 + 3) * 4
[ 20 ]:
```
### Unary minus
```
[ 0 ]: -5
[ -5 ]:
[ 0 ]: -(3 + 2)
[ -5 ]:
```
---
## Ergonomics
### Percentage operator
`N%` means *N percent of the accumulator* — a postfix operator that expands to `N * acc / 100`:
```
[ 1500 ]: 20%
[ 300 ]: (20% of 1500)
[ 1500 ]: + 20%
[ 1800 ]: (1500 + 20% of 1500)
[ 1800 ]: - 10%
[ 1620 ]: (1800 − 10% of 1800)
```
`%` still works as **modulo** when followed by a number or expression:
```
[ 0 ]: 17 % 5
[ 2 ]:
```
### Implicit multiplication
A number or closing parenthesis immediately before `(` multiplies without an explicit `*`:
```
[ 0 ]: 2(3 + 1)
[ 8 ]:
[ 0 ]: (2 + 1)(4 - 1)
[ 9 ]:
```
---
## Constants
| `pi` | 3.14159265358979... |
| `e` | 2.71828182845904... |
| `acc` | Current accumulator value |
`acc` is an explicit alias for the accumulator — useful when you need it in the middle of an expression:
```
[ 9 ]: acc * 2 + 1
[ 19 ]:
[ 25 ]: sqrt(acc)
[ 5 ]:
```
---
## Math functions
All functions take a single argument in parentheses. If called with **empty parentheses**, the accumulator is used as the argument.
| `sqrt(x)` | Square root |
| `abs(x)` | Absolute value |
| `floor(x)` | Round down |
| `ceil(x)` | Round up |
| `round(x)` | Round to nearest |
| `log(x)` | Base-10 logarithm |
| `ln(x)` | Natural logarithm |
| `exp(x)` | *e* raised to *x* |
| `sin(x)` | Sine (radians) |
| `cos(x)` | Cosine (radians) |
| `tan(x)` | Tangent (radians) |
```
[ 0 ]: sqrt(144)
[ 12 ]:
[ 0 ]: sin(pi / 6)
[ 0.5 ]:
[ 0 ]: log(1000)
[ 3 ]:
[ 16 ]: sqrt() same as sqrt(16)
[ 4 ]:
[ 4 ]: sqrt(acc) same as sqrt(4)
[ 2 ]:
```
Functions can be nested and combined:
```
[ 0 ]: sqrt(abs(-25))
[ 5 ]:
[ 0 ]: round(sin(pi / 3) * 100) / 100
[ 0.87 ]:
```
---
## Memory cells
Nine memory cells: `m1` through `m9`. Values persist for the duration of the session and can be saved to disk with `ms` and restored with `ml`.
### Store
| `m1` | Store accumulator into `m1` |
| `expr m1` | Evaluate expression, store result into `m1` |
```
[ 0 ]: 42
[ 42 ]: m1 m1 = 42
[ 0 ]: (10 + 5) * 2 m2 m2 = 30
[ 30 ]:
```
### Recall
Use `m1`–`m9` as values anywhere inside an expression:
```
[ 0 ]: m1 + m2
42 + 30
[ 72 ]: (42 + 30)
[ 0 ]: m1 * 2 + m2 / 3
42 * 2 + 30 / 3
[ 94 ]:
```
When memory references are expanded, the substituted expression is printed before the result:
```
[ 0 ]: m1 + 8 + m1
6 + 8 + 6
[ 20 ]:
```
### Compound assignment
`expr m[1-9]OP` evaluates `cell OP expr`, stores the result back into the cell, and sets the accumulator to the new cell value.
| `expr m1+` | `m1 += expr` |
| `expr m1-` | `m1 -= expr` |
| `expr m1*` | `m1 *= expr` |
| `expr m1/` | `m1 /= expr` |
| `expr m1%` | `m1 %= expr` |
| `expr m1^` | `m1 ^= expr` |
```
[ 0 ]: 100 m1 m1 = 100; accumulator = 100
[ 100 ]: 2 m1* m1 = 200; accumulator = 200
[ 200 ]: 50 m1- m1 = 150; accumulator = 150
[ 150 ]: 3 m1/ m1 = 50; accumulator = 50
```
The expression itself can be anything, including memory references:
```
[ 0 ]: m2 m1+ m1 = m1 + m2
[ 0 ]: 1 m1+ m1 += 1 (increment)
```
### Copy cell to cell
```
[ 0 ]: m1 m2 store value of m1 into m2
```
### View, clear, and persist
| `m` | Show all non-zero cells |
| `mc` | Clear all cells |
| `mc1` | Clear cell `m1` |
| `ms` | Save all cells to `~/.config/ccalc/memory.toml` |
| `ml` | Load cells from file (clears current cells first) |
```
[ 10 ]: m
m1: 85
m2: 30
[ 10 ]: mc1
[ 10 ]: mc
```
---
## REPL commands
| `q` | Quit |
| `c` | Clear accumulator (reset to 0) |
| `cls` | Clear the screen |
| `p` | Show current decimal precision |
| `p<N>` | Set decimal precision (0–15) |
| `hex` / `dec` / `bin` / `oct` | Switch display base |
| `base` | Show accumulator in all four bases |
| `ms` | Save memory cells to disk |
| `ml` | Load memory cells from disk |
| Ctrl+C / Ctrl+D | Quit |
## Keyboard shortcuts
| ↑ / ↓ | Browse input history |
| Ctrl+R | Reverse history search |
| ← / → / Home / End | Cursor movement |
| Ctrl+W | Delete word before cursor |
| Ctrl+U | Clear line |
---
## Number formatting and bases
### Decimal precision
By default results are shown with up to 10 decimal digits, trailing zeros removed:
```
[ 0 ]: 1 / 3
[ 0.3333333333 ]:
[ 0 ]: p4
[ 0 ]: 1 / 3
[ 0.3333 ]:
[ 0 ]: p show current precision
precision: 4
```
`p<N>` sets N decimal places (0–15). `p` alone shows the current setting.
Very large (`|n| >= 1e15`) and very small (`|n| < 1e-9`) numbers switch to scientific notation automatically:
```
[ 0 ]: 2 ^ 60
[ 1.152921504606847e18 ]:
```
### Number bases
**Input literals** — mix bases freely in any expression:
| `0x` | hex | `0xFF` → 255 |
| `0b` | binary | `0b1010` → 10 |
| `0o` | octal | `0o17` → 15 |
**Display base** — controls how the prompt and results are shown:
| `hex` | Switch display to hexadecimal |
| `dec` | Switch display to decimal (default) |
| `bin` | Switch display to binary |
| `oct` | Switch display to octal |
| `base` | Show accumulator in all four bases |
```
[ 0 ]: 0xFF + 0b1010
[ 265 ]: hex
[ 0x109 ]: + 0b10
[ 0x10B ]: dec
[ 267 ]:
```
**Inline base suffix** — evaluate an expression and switch display base in one step:
```
[ 0 ]: 0xFF + 0b1010 hex
[ 0x109 ]:
```
**`base` command:**
```
[ 10 ]: base
2 - 0b1010
8 - 0o12
10 - 10
16 - 0xA
```
**Expression conversion** — when the display base is non-decimal and the expression contains literals in other bases, the converted expression is printed before the result:
```
[ 0x6 ]: 0b11 + 0b11
0x3 + 0x3
[ 0x6 ]:
[ 0b110 ]: 2 + 0b110 + 0xa
0b10 + 0b110 + 0b1010
[ 0b10010 ]:
```
---
## Examples
**Percentage — add VAT:**
```
[ 0 ]: 1200
[ 1200 ]: + 20%
[ 1440 ]: (1200 + 20% of 1200)
```
**Percentage — discount:**
```
[ 0 ]: 850
[ 850 ]: - 15%
[ 722.5 ]: (850 − 15% of 850)
```
**Implicit multiplication:**
```
[ 0 ]: 2(3 + 1)
[ 8 ]:
[ 0 ]: (2 + 1)(4 - 1)
[ 9 ]:
```
**Compound interest** — 1000 at 7% for 10 years:
```
[ 0 ]: 1000 * 1.07 ^ 10
[ 1967.15135729 ]:
```
**Pythagorean hypotenuse** — sides 3 and 4:
```
[ 0 ]: sqrt(3^2 + 4^2)
[ 5 ]:
```
**Running budget** — track a total across multiple entries:
```
[ 0 ]: 1200 m1 budget in m1
[ 1200 ]: m1 - 350 m1 spent 350 → m1 = 850
1200 - 350
[ 850 ]: 80 m1- spent 80 → m1 = 770
850 - 80
[ 770 ]: m
m1: 770
```
**Angle conversion** — degrees to radians, then sine:
```
[ 0 ]: 30 * pi / 180
[ 0.5235987756 ]: sin()
[ 0.5 ]:
```
**Storing intermediate results**:
```
[ 0 ]: sqrt(2) m1 store √2
[ 1.4142135624 ]: m1 ^ 10
[ 32 ]: (√2)^10 = 2^5 = 32
```
---
## Script files
When reading from a file (`ccalc < formula.txt`) you have three tools to control output:
### Comments
A `#` starts a comment. It can be the first character on the line (full-line comment) or appear after an expression (inline comment). Everything from `#` to end-of-line is ignored.
```
# Cylinder volume: V = pi * r^2 * h
pi * 5^2 # pi * r^2, r = 5
```
### Semicolon — suppress output
A trailing `;` evaluates the expression and updates the accumulator, but prints nothing. Use it to silence intermediate steps.
```
0.06 / 12; # monthly rate — silent
m1; # save to m1 — silent
1 + m1;
^ 360; # (1 + r)^360 — silent
```
### `print` — explicit output
`print` prints the current accumulator value. `print "label"` prints the label followed by the value. Write any punctuation you want directly in the label.
| `print` | `1199.101050304` |
| `print "Monthly payment ($):"` | `Monthly payment ($): 1199.101050304` |
**Section headers** — `print "label"` placed after a blank line (with no expression between the blank line and the `print`) prints the label only, without a value. Use this for headings between calculation blocks:
```
print "=== Resistors in series ==="
100 + 220 + 470
print "Total resistance (Ohm):"
print "=== Parallel combination ==="
1/100 + 1/220;
^ -1
print "Parallel resistance (Ohm):"
```
Output:
```
=== Resistors in series ===
790
Total resistance (Ohm): 790
=== Parallel combination ===
68.7500002148
Parallel resistance (Ohm): 68.7500002148
```
### Example files
The `examples/` directory contains annotated formula files ready to run:
| `cylinder.ccalc` | Volume and surface area of a cylinder |
| `mortgage.ccalc` | Monthly mortgage payment |
| `data_storage.ccalc` | Real GiB capacity of a "500 GB" drive |
| `resistors.ccalc` | Series, parallel resistance, voltage divider, power |
```bash
ccalc < examples/mortgage.ccalc
```
---
## Building and testing
```bash
cargo build # debug build
cargo build --release # optimized build
cargo test # run all tests
```
---
## Project structure
```
src/
main.rs — entry point, mode detection (arg / pipe / REPL), CLI flags
repl.rs — REPL loop, run_pipe(), run_expr(), shared evaluate() core
parser.rs — lexer (tokenizer) + recursive descent parser
eval.rs — AST types (Expr, Op) + evaluator + number formatters + Base enum
memory.rs — memory cells, command parser, directive extractor, ref expander
Cargo.toml — manifest (single source of truth for version)
CHANGELOG.md — version history
```
---
## License
MIT