Skip to content

Commit 8ee0b4c

Browse files
authored
[cli] Allow negative numbers + add range, value name, argument group validation + custom usage line (#201)
This PR enhances the `decimo` CLI calculator’s argument parsing and help/usage UX by leveraging newer ArgMojo features, and updates project documentation/plans accordingly. **Changes:** - Add ArgMojo metadata to CLI options/flags (value names, numeric range validation, help groups) and set a custom usage line. - Enable ArgMojo negative-number passthrough for positional expressions. - Update docs/plans to reflect ArgMojo v0.5.0 integration and adjust pixi tasks for fetching ArgMojo source.
1 parent 5aa3550 commit 8ee0b4c

File tree

4 files changed

+106
-111
lines changed

4 files changed

+106
-111
lines changed

docs/plans/cli_calculator.md

Lines changed: 85 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,19 @@ Rows are sorted by implementation priority for `decimo` (top = implement first).
2222
| 3 | Large integers (arbitrary) ||||||||| 1 |
2323
| 4 | Pipeline/Batch scripting ||||||||| 1 |
2424
| 5 | Built-in math functions ||||||||| 2 |
25-
| 6 | Interactive REPL ||||||||| 3 |
26-
| 7 | Variables/State ||||||||| 3 |
27-
| 8 | Unit conversion ||||||||| 4 |
28-
| 9 | Matrix/Linear algebra ||||||||| 4 |
29-
| 10 | Symbolic computation ||||||||| 4 |
25+
| 6 | Interactive REPL ||||||||| 4 |
26+
| 7 | Variables/State ||||||||| 4 |
27+
| 8 | Unit conversion ||||||||| 5 |
28+
| 9 | Matrix/Linear algebra ||||||||| 5 |
29+
| 10 | Symbolic computation ||||||||| 5 |
3030

3131
**Priority rationale:**
3232

3333
1. **Basic arithmetic + High-precision + Large integers + Pipeline** (Phase 1) — These are the raison d'être of `decimo`. Decimo already provides arbitrary-precision `BigDecimal`; wiring up tokenizer → parser → evaluator gives immediate value. Pipeline/batch is nearly free once one-shot works (just loop over stdin lines).
3434
2. **Built-in math functions** (Phase 2) — `sqrt`, `ln`, `exp`, `sin`, `cos`, `tan`, `root` already exist in the Decimo API. Adding them mostly means extending the tokenizer/parser to recognize function names.
35-
3. **Interactive REPL + Variables/State** (Phase 3) — Valuable for exploration, but requires a read-eval-print loop, `ans` tracking, named variable storage and session-level precision management. More engineering effort, less urgency.
36-
4. **Unit conversion / Matrix / Symbolic** (Phase 4) — Out of scope. `decimo` is a numerical calculator, not a CAS or unit library. These can be revisited if there is demand.
35+
3. **Polish & ArgMojo integration** (Phase 3) — Error diagnostics, edge-case handling, and exploiting ArgMojo v0.5.0 features (shell completions, argument groups, numeric range validation, etc.). Mostly CLI UX refinement.
36+
4. **Interactive REPL + Subcommands** (Phase 4) — Requires a read-eval-print loop, `ans` tracking, named variable storage, session-level precision management, and CLI restructuring with subcommands. More engineering effort, less urgency.
37+
5. **Future enhancements** (Phase 5) — CJK full-width detection, response files, unit conversion, matrix, symbolic. Out of scope for now.
3738

3839
## Usage Design
3940

@@ -161,15 +162,23 @@ Best for: interactive exploration, multi-step calculations, experimenting with p
161162

162163
### Layer 1: ArgMojo — CLI Argument Parsing
163164

164-
ArgMojo handles the outer CLI structure. No modifications to ArgMojo are needed.
165+
ArgMojo handles the outer CLI structure via its struct-based declarative API (`Parsable` trait).
165166

166167
```mojo
167-
var cmd = Command("decimo", "Arbitrary-precision CLI calculator.", version="0.1.0")
168-
cmd.add_arg(Arg("expr", help="Math expression").positional().required())
169-
cmd.add_arg(Arg("precision", help="Decimal precision").long("precision").short("p").default("50"))
170-
cmd.add_arg(Arg("sci", help="Scientific notation").long("sci").flag())
171-
cmd.add_arg(Arg("eng", help="Engineering notation").long("eng").flag())
172-
cmd.add_arg(Arg("pad", help="Pad trailing zeros to precision").long("pad-to-precision").flag())
168+
from argmojo import Parsable, Option, Flag, Positional, Command
169+
170+
struct DecimoArgs(Parsable):
171+
var expr: Positional[String, help="Math expression to evaluate", required=True]
172+
var precision: Option[Int, long="precision", short="p", help="Number of significant digits",
173+
default="50", value_name="N", has_range=True, range_min=1, range_max=1000000000, group="Computation"]
174+
var scientific: Flag[long="scientific", short="s", help="Output in scientific notation", group="Formatting"]
175+
var engineering: Flag[long="engineering", short="e", help="Output in engineering notation", group="Formatting"]
176+
var pad: Flag[long="pad", short="P", help="Pad trailing zeros to the specified precision", group="Formatting"]
177+
var delimiter: Option[String, long="delimiter", short="d", help="Digit-group separator",
178+
default="", value_name="CHAR", group="Formatting"]
179+
var rounding_mode: Option[String, long="rounding-mode", short="r", help="Rounding mode",
180+
default="half-even", choices="half-even,half-up,half-down,up,down,ceiling,floor",
181+
value_name="MODE", group="Computation"]
173182
```
174183

175184
### Layer 2: Tokenizer — Lexical Analysis
@@ -290,34 +299,50 @@ Format the final `BigDecimal` result based on CLI flags:
290299
| 2.9 | `--delimiter` / `-d` (digit grouping) || Extra feature beyond original plan |
291300
| 2.10 | `--rounding-mode` / `-r` || 7 modes; extra feature beyond original plan |
292301

293-
### Phase 3: Polish
302+
### Phase 3: Polish & ArgMojo Deep Integration
303+
304+
> ArgMojo v0.5.0 is installed via pixi. Reference: <https://github.com/forfudan/argmojo> · [User Manual](https://github.com/forfudan/argmojo/wiki)
294305
295306
1. Error messages: clear diagnostics for malformed expressions (e.g., "Unexpected token '*' at position 5").
296307
2. Edge cases: division by zero, negative sqrt, overflow, empty expression.
297-
3. Upgrade to ArgMojo v0.2.0 (once available in pixi). See [ArgMojo v0.2.0 Upgrade Tasks](#argmojo-v020-upgrade-tasks) below.
298-
4. Performance: ensure the tokenizer/parser overhead is negligible compared to BigDecimal computation.
299-
5. Documentation and examples in README.
300-
6. Build and distribute as a single binary.
301-
302-
| # | Task | Status | Notes |
303-
| --- | ---------------------------------------------------------------------- | :----: | --------------------------------------------- |
304-
| 3.1 | Error messages with position info + caret display || Colored stderr: red `Error:`, green `^` caret |
305-
| 3.2 | Edge cases (div-by-zero, negative sqrt, empty expression, etc.) || 27 error-handling tests |
306-
| 3.3 | ArgMojo v0.2.0 upgrade (`add_tip()`, `allow_negative_numbers()`, etc.) | ? | Blocked: waiting for ArgMojo v0.2.0 in pixi |
307-
| 3.4 | Performance validation || No CLI-level benchmarks yet |
308-
| 3.5 | Documentation (user manual for CLI) || Will be `docs/user_manual_cli.md` |
309-
| 3.6 | Build and distribute as single binary || |
310-
311-
### Phase 4: Interactive REPL
312-
313-
1. Read-eval-print loop: read a line from stdin, evaluate, print result, repeat.
314-
2. Custom prompt (`decimo>`).
315-
3. `ans` variable to reference the previous result.
316-
4. Variable assignment: `x = sqrt(2)`, usable in subsequent expressions.
317-
5. Session-level precision: settable via `decimo -p 100` at launch or `:precision 100` command mid-session.
318-
6. Graceful exit: `exit`, `quit`, `Ctrl-D`.
319-
7. Clear error messages without crashing the session (e.g., "Error: division by zero", then continue).
320-
8. History (if Mojo gets readline-like support).
308+
3. Upgrade to ArgMojo v0.5.0 and adopt declarative API.
309+
4. Exploit ArgMojo v0.5.0 features: shell completions, numeric validation, help readability, argument groups.
310+
5. Performance: ensure the tokenizer/parser overhead is negligible compared to BigDecimal computation.
311+
6. Documentation and examples in README (include shell completion setup).
312+
7. Build and distribute as a single binary.
313+
314+
| # | Task | Status | Notes |
315+
| ---- | --------------------------------------------------------------- | :----: | ------------------------------------------------------------------------------------------------------------------ |
316+
| 3.1 | Error messages with position info + caret display || Colored stderr: red `Error:`, green `^` caret |
317+
| 3.2 | Edge cases (div-by-zero, negative sqrt, empty expression, etc.) || 27 error-handling tests |
318+
| 3.3 | ArgMojo v0.5.0 declarative API migration || `Parsable` struct, `add_tip()`, `mutually_exclusive()`, `choices`, `--version` all working |
319+
| 3.4 | Built-in features (free with ArgMojo v0.5.0) || Typo suggestions, `NO_COLOR`, CJK full-width correction, prefix matching, `--` stop marker |
320+
| 3.5 | Shell completion (`--completions bash\|zsh\|fish`) || Built-in — zero code; needs documentation in user manual and README |
321+
| 3.6 | `allow_negative_numbers()` to allow pure negative numbers || Explicit opt-in in hybrid bridge; `decimo "-3"` works, expressions need quoting or `--` |
322+
| 3.7 | Numeric range on `precision` || `has_range=True, range_min=1, range_max=1000000000`; rejects `--precision 0` or `-5` |
323+
| 3.8 | Value names for help readability || `--precision <N>`, `--delimiter <CHAR>`, `--rounding-mode <MODE>` |
324+
| 3.9 | Argument groups in help output || `Computation` and `Formatting` groups in `--help` |
325+
| 3.10 | Custom usage line || `Usage: decimo [OPTIONS] <EXPR>` |
326+
| 3.11 | `Parsable.run()` override || Move eval logic into `DecimoArgs.run()` for cleaner separation |
327+
| 3.12 | Performance validation || No CLI-level benchmarks yet |
328+
| 3.13 | Documentation (user manual for CLI) || `docs/user_manual_cli.md`; include shell completion setup |
329+
| 3.14 | Build and distribute as single binary || |
330+
| 3.15 | Allow negative expressions || This needs ArgMojo to regard arguments with a hyphen and followed by more than one letter as a positional argument |
331+
332+
### Phase 4: Interactive REPL & Subcommands
333+
334+
1. Restructure CLI with subcommands: `decimo eval "expr"` (default), `decimo repl`, `decimo help functions`.
335+
2. Persistent flags (`--precision`, `--scientific`, etc.) across subcommands.
336+
3. Subcommand dispatch via `parse_full()`.
337+
4. No-args + TTY detection → launch REPL directly.
338+
5. Read-eval-print loop: read a line from stdin, evaluate, print result, repeat.
339+
6. Custom prompt (`decimo>`).
340+
7. `ans` variable to reference the previous result.
341+
8. Variable assignment: `x = sqrt(2)`, usable in subsequent expressions.
342+
9. Session-level precision: settable via `decimo -p 100` at launch or `:precision 100` command mid-session.
343+
10. Graceful exit: `exit`, `quit`, `Ctrl-D`.
344+
11. Clear error messages without crashing the session (e.g., "Error: division by zero", then continue).
345+
12. History (if Mojo gets readline-like support).
321346

322347
```bash
323348
$ decimo
@@ -334,80 +359,32 @@ Error: division by zero
334359
decimo> exit
335360
```
336361

337-
| # | Task | Status | Notes |
338-
| --- | ---------------------------------------- | :----: | ----- |
339-
| 4.1 | Read-eval-print loop || |
340-
| 4.2 | Custom prompt (`decimo>`) || |
341-
| 4.3 | `ans` variable (previous result) || |
342-
| 4.4 | Variable assignment (`x = expr`) || |
343-
| 4.5 | Session-level precision (`:precision N`) || |
344-
| 4.6 | Graceful exit (`exit`, `quit`, Ctrl-D) || |
345-
| 4.7 | Error recovery (don't crash session) || |
362+
| # | Task | Status | Notes |
363+
| ---- | ---------------------------------------- | :----: | --------------------------------------------------------------------------------------------------------------- |
364+
| 4.1 | Subcommand restructure || `decimo eval "expr"` (default), `decimo repl`, `decimo help functions`; use `subcommands()` hook |
365+
| 4.2 | Persistent flags across subcommands || `precision`, `--scientific`, etc. as `persistent=True`; both `decimo repl -p 100` and `decimo -p 100 repl` work |
366+
| 4.3 | `parse_full()` for subcommand dispatch || Typed struct + `ParseResult.subcommand` for dispatching to eval/repl/help handlers |
367+
| 4.4 | No-args + TTY → launch REPL directly || Replace `help_on_no_arguments()` with REPL auto-launch when terminal detected |
368+
| 4.5 | Read-eval-print loop || |
369+
| 4.6 | Custom prompt (`decimo>`) || |
370+
| 4.7 | `ans` variable (previous result) || |
371+
| 4.8 | Variable assignment (`x = expr`) || |
372+
| 4.9 | Session-level precision (`:precision N`) || |
373+
| 4.10 | Graceful exit (`exit`, `quit`, Ctrl-D) || |
374+
| 4.11 | Error recovery (don't crash session) || |
375+
| 4.12 | Interactive prompting for missing values || Use `.prompt()` on subcommand args for interactive precision input, etc. |
376+
| 4.13 | Subcommand aliases || `command_aliases(["e"])` for `eval`, `command_aliases(["r"])` for `repl` |
377+
| 4.14 | Hidden subcommands || Hide `debug` / internal subcommands from help |
346378

347379
### Phase 5: Future Enhancements
348380

349381
1. Detect full-width digits/operators for CJK users while parsing.
382+
2. Response files (`@expressions.txt`) — when Mojo compiler bug is fixed, use ArgMojo's `cmd.response_file_prefix("@")`.
350383

351-
### ArgMojo v0.2.0 Upgrade Tasks
352-
353-
> **Prerequisite:** ArgMojo ≥ v0.2.0 is available as a pixi package.
354-
>
355-
> Reference: <https://github.com/forfudan/argmojo/releases/tag/v0.2.0>
356-
357-
Once ArgMojo v0.2.0 lands in pixi, apply the following changes to `decimo`:
358-
359-
#### 1. Auto-show help when no positional arg is given
360-
361-
ArgMojo v0.2.0 automatically displays help when a required positional argument is missing — no code change needed on our side. Remove the `.required()` guard if it interferes, or verify the behaviour works out of the box.
362-
363-
**Current (v0.1.x):** missing `expr` prints a raw error.
364-
**After:** missing `expr` prints the full help text.
365-
366-
#### 2. Shell-quoting tips via `add_tip()`
367-
368-
Replace the inline description workaround with ArgMojo's dedicated `add_tip()` API. Tips render as a separate section at the bottom of `--help` output.
369-
370-
```mojo
371-
cmd.add_tip('If your expression contains *, ( or ), wrap it in quotes:')
372-
cmd.add_tip(' decimo "2 * (3 + 4)"')
373-
cmd.add_tip('Or use noglob: noglob decimo 2*(3+4)')
374-
cmd.add_tip("Or add to ~/.zshrc: alias decimo='noglob decimo'")
375-
```
376-
377-
Remove the corresponding note that is currently embedded in the `Command` description string.
378-
379-
#### 3. Negative number passthrough
380-
381-
Enable `allow_negative_numbers()` so that expressions like `decimo -3+4` or `decimo -3.14` are treated as math, not as unknown CLI flags.
382-
383-
```mojo
384-
cmd.allow_negative_numbers()
385-
```
386-
387-
#### 4. Rename `Arg``Argument`
388-
389-
`Arg` is kept as an alias in v0.2.0, so this is optional but recommended for consistency with the new API naming.
390-
391-
```mojo
392-
# Before
393-
from argmojo import Arg, Command
394-
# After
395-
from argmojo import Argument, Command
396-
```
397-
398-
#### 5. Colored error messages from ArgMojo
399-
400-
ArgMojo v0.2.0 produces ANSI-colored stderr errors for its own parse errors (e.g., unknown flags). Our custom `display.mojo` colors still handle calculator-level errors. Verify that both layers look consistent (same RED styling).
401-
402-
#### 6. Subcommands (Phase 4 REPL prep)
403-
404-
Although not needed immediately, the new `add_subcommand()` API could later support:
405-
406-
- `decimo repl` — launch interactive REPL
407-
- `decimo eval "expr"` — explicit one-shot evaluation (current default)
408-
- `decimo help <topic>` — extended help on functions, constants, etc.
409-
410-
This is deferred to Phase 4 planning.
384+
| # | Task | Status | Notes |
385+
| --- | ------------------------------------------- | :----: | ------------------------------------------------------------------------------ |
386+
| 5.1 | Full-width digit/operator detection for CJK || Tokenizer-level handling for CJK users |
387+
| 5.2 | Response files (`@expressions.txt`) || Blocked on Mojo compiler bug; `cmd.response_file_prefix("@")` ready when fixed |
411388

412389
## Design Decisions
413390

0 commit comments

Comments
 (0)