summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2026-05-24refactor(repl): introduce RPNCalculator interface to fix DIP violation (task rj)Paul Buetow
Handlers accessed repl.rpnState.rpnCalc — three levels of concrete types violating DIP and Law of Demeter. Define an RPNCalculator interface capturing only the methods handlers actually need, and expose it via REPL.RpnCalculator() so handlers depend on the interface, not the concrete chain of *rpn.RPN.
2026-05-24fix: remove dead "=" entry from operator registry (fj)Paul Buetow
The dispatchToken guard for standalone "=" always fires before the registry lookup, making the "=" registration in operator_registry.go dead code. Assignment with "=" is handled at the expression level by handleStandardAssign, not via the stack-level operator registry. Also update related tests: - TestAssignmentOperatorRegistry: remove "=" from registered operators - TestResultStackErrors: "=" is no longer an operator, expects "unknown token" instead of "insufficient operands"
2026-05-24refactor(rpn): split NewOperatorRegistry() into focused helpers (task ej)Paul Buetow
NewOperatorRegistry() was 60+ lines of registration calls. Split into six category-specific helpers: registerArithmeticOperators, registerComparisonOperators, registerStackOperators, registerVariableOperators, registerCommandOperators, and registerHyperOperators. NewOperatorRegistry() is now a thin ~13-line orchestrator.
2026-05-24fix(rpn): optimize Swap from O(n) to O(1) (task dj)Paul Buetow
Replace stack.Values() with direct Pop() calls in Swap(). Previously created a full stack copy just to read the top two values. Behavior is unchanged — ensureStackLength guard guarantees pops succeed.
2026-05-24refactor(rpn): extract sortVariableInfos helper (task cj)Paul Buetow
Deduplicate identical VariableInfo sorting logic across ListVariables(), formatVariablesUnsafe(), and Save(). All three callers now use a single unexported sortVariableInfos helper.
2026-05-24refactor(repl): extract evalWithStackRestore helper (task bj)Paul Buetow
Factor out the repeated save-restore-evaluate pattern from RPNHandler.Handle() into a single helper method so the stack is always restored on parse errors, across all call sites.
2026-05-24rpn: decouple RPN from concrete *Operations type (task aj)Paul Buetow
Define OperationsProvider interface composed of focused sub-interfaces (ModeController, MetricCommander, CustomMetricManager) plus the existing StackOperator. Change RPN.ops from *Operations to OperationsProvider so the high-level RPN module depends on abstractions, not the concrete type (DIP). Add compile-time assertions for the new interfaces in operations.go.
2026-05-24refactor(rpn): split ArithmeticOperator into 3 interfaces (task 9j)Paul Buetow
The ArithmeticOperator interface bundled basic arithmetic (+, -, *, /, ^, %), logarithmic operations (Log2, Log10, Ln), and metric conversion (Convert) into a single interface, violating ISP and LSP. Split into: - ArithmeticOperator — Add, Subtract, Multiply, Divide, Power, Modulo - LogarithmicOperator — Log2, Log10, Ln - MetricOperator — Convert Updated compile-time interface checks in operations.go and the trailing comment in operations_interfaces.go. No behavioral change.
2026-05-24refactor(rpn): split large functions in rpn_parse.go (task 6j)Paul Buetow
Extract inline helper methods to bring dispatchToken, handleMetricCommand, and handleCustomCommand under 50 lines: - handleInlineAssignment: extracts := / =: stack assignment logic - handleMetricPrefix: handles metric binary/decimal prefix mode switching - handleCustomDefine: handles 'custom define' subcommand - handleCustomUndefine: handles 'custom undefine' subcommand dispatchToken: 55 -> 47 lines handleMetricCommand: 40 -> 38 lines handleCustomCommand: 43 -> 31 lines
2026-05-24refactor(rpn): simplify isValidIdentifier(), removing dead multi-char loop ↵Paul Buetow
(task 5j) The loop checking remaining characters was dead code — the final check meant any token longer than 1 char was rejected regardless of what the loop found. Replace the entire function with a simple single-char check.
2026-05-24fix(rpn): remove duplicate ConstantsProvider initialization (task 4j)Paul Buetow
NewOperations() no longer creates a ConstantsProvider internally since NewRPN() always calls SetConstants() immediately after. Eliminates a wasted allocation with no behavioral change.
2026-05-24refactor(rpn): extract checkStackOverflow helper in pushLiteral (#3j)Paul Buetow
Deduplicate the repeated stack overflow check in pushLiteral() by extracting it into a checkStackOverflow() helper method. Replaces three identical inline checks with calls to the new helper.
2026-05-24rpn: remove unused stack parameter from shouldPushName (task 2j)Paul Buetow
2026-05-24docs: add CLI usage guide (task di)Paul Buetow
Document all invocation modes (single-expression, REPL, pipe, stdin), boolean coercion rules, version/help behavior, exit codes, and practical use cases for scripts and CI pipelines.
2026-05-24docs: add symbols.md — document :x syntax and symbol behavior (task ci)Paul Buetow
2026-05-24docs: add repl-mode.md documentation (task bi)Paul Buetow
2026-05-24docs: add rational-mode.md documentation (task ai)Paul Buetow
Test and document rational number mode: - rat on/off/toggle commands (REPL only) - How big.Rat integration works internally - Precision comparison examples with actual output - Known limitation: +, -, % fail for non-dyadic decimals (0.1, 0.2) due to Rat.Float64() rejecting lossy conversions - Performance trade-offs vs float64 mode - Edge cases including metrics, constants, and variables - When to use rational mode and when to stick with float mode
2026-05-24docs: add custom-metrics.md (task 9i)Paul Buetow
Document custom metric commands (define/undefine/list/show) with syntax, REPL workflow examples, arithmetic usage, practical use cases, and edge cases including duplicate names, invalid categories, and factor zero behavior.
2026-05-24docs: add metric-commands.md (task 8i)Paul Buetow
Document all metric subcommands with real CLI output examples: - metric show: inspect metric info for top of stack - metric list: list all metric categories - metric <category>: list units in a category - metric compatible: check compatibility of two values - metric decimal set / metric binary set: SI vs IEC modes Covers REPL vs single-command differences, SI vs IEC prefix modes, and practical use cases for exploring and verifying metrics.
2026-05-24docs: add unit-conversion.md (task 7i)Paul Buetow
Document the @prefix convert syntax with real examples for all metric categories (DataRate, DataSize, Time, Weight, Speed, Distance, Universal). Includes practical use cases for bandwidth planning, travel, cooking, and data storage, plus edge case coverage for incompatible categories, unknown metrics, Cool absorbing, and SI/IEC interoperability.
2026-05-24repl: consolidate single-token handling in RPNHandlerPaul Buetow
Merge three identical single-token blocks (operator, number, symbol) into one block with conditional checks. Eliminates redundant strings.Fields calls and len(fields)==1 checks. The 'rat' command early-return is not redundant — it correctly short-circuits before ExecuteCommand is reached.
2026-05-24rpn: fix double-prefix error messages in FastPowerPaul Buetow
toFloat64 already includes the op name in its error message, so wrapping with buildError produced double prefixes like '**: **: value X is not numeric'. - Exponent check: use buildError with plain errors.New - Base check: return toFloat64 error directly (already has context)
2026-05-24rpn: complete nAry helper refactor for HyperMultiply and HyperModuloPaul Buetow
- Extend nAryMetricOp with optional per-operand validate callback (used by HyperModulo for modulo-by-zero check) - HyperMultiply: use nAryScalarOp instead of manual multiplication - HyperModulo: use nAryMetricOp with validate callback instead of inline metric resolution and conversion HyperModulo reduced from ~45 to ~5 lines. All tests pass.
2026-05-24rpn: remove exported Number type aliasPaul Buetow
type Number = NumericValue was a legacy alias that created confusion — callers could use Number or NumericValue interchangeably. Removed the alias and updated: - NewNumber/NewNumberWithMetric return types: Number -> NumericValue - Comments referencing Number now say NumericValue The alias added no value since Number was never used as a distinct type anywhere in the codebase.
2026-05-24rpn: remove combined Operator interface per ISPPaul Buetow
The Operator interface (~40 methods) embedded 7 sub-interfaces plus mode/prefix/metric/custom methods. Since RPN is the sole client and *Operations is the sole implementor, the combined interface added indirection without practical benefit. Changes: - Remove type Operator entirely - RPN.ops: Operator -> *Operations (concrete type) - NewOperatorRegistry: Operator -> *Operations - Compile-time checks: one per sub-interface (7 checks) - Sub-interfaces preserved for documentation/organization
2026-05-24rpn: eliminate duplicate ParseFloat in pushLiteralPaul Buetow
pushLiteral called strconv.ParseFloat twice for non-RationalMode input: once to check if token is a number, once to get the value. Capture the parsed value from the first call to avoid redundant parsing.
2026-05-24rpn: fix double-prefix in logOp error messagePaul Buetow
buildError(opName, ...) prepends opName to the error, so the inner error should not repeat it. Changed from: buildError(opName, fmt.Errorf("%s undefined...", opName)) to: buildError(opName, errors.New("undefined...")) Produces "lg: undefined for non-positive numbers" instead of "lg: lg undefined for non-positive numbers".
2026-05-24rpn: split evaluate() into focused helpersPaul Buetow
Extract evaluate() (~130 lines) into: - evaluate(): setup and orchestration (20 lines) - evaluateTokens(): token loop with handled tracking (12 lines) - dispatchToken(): single token dispatch (58 lines) - checkVariableName(): variable name detection for assignment (21 lines) - processResult(): final stack state and result formatting (25 lines) All helpers under ~50 lines except dispatchToken (58, close). Behavior preserved - all tests pass.
2026-05-24rpn: split handleOperator into focused helpersPaul Buetow
Extract handleOperator (~65 lines) into: - checkAndPushSymbol(): :x syntax check and symbol push - resolveVariableOrConstant(): variable and constant lookups - dispatchOperator(): operator dispatch with symbol fallback All helpers under 30 lines. Behavior preserved - all tests pass.
2026-05-24rpn: extract nAryMetricOp and nAryScalarOp helpers in hyper opsPaul Buetow
Extract shared patterns from hyper operators: - nAryMetricOp: metric resolution, category validation, base unit conversion, binary fn application, result conversion (used by HyperAdd, HyperSubtract) - nAryScalarOp: applies binary fn to pre-converted float64 values and pushes with Cool metric (used by HyperDivide, HyperPower) HyperMultiply stays inline (unique init-to-1 pattern). HyperModulo stays inline (metric-aware with modulo-by-zero check). Reduces operations_hyper.go from ~300 to ~268 lines.
2026-05-24rpn: remove redundant mutex locking in RPN mode/prefix wrappersPaul Buetow
RPN.GetMode/SetMode/GetPrefixMode/SetPrefixMode acquired r.mu before delegating to Operations methods that already acquire o.mu. Since Operations guards mode and prefixMode with its own RWMutex, the RPN-level locks were redundant double-locking on separate mutexes for the same data. Removed the r.mu locking from these four methods. Thread safety is preserved by Operations' internal mutex.
2026-05-24rpn: use strings.Builder in Show() for loop concatenationPaul Buetow
Replace += string concatenation in Show() with strings.Builder for O(n) performance instead of O(n^2). ListConstants() already used strings.Builder.
2026-05-24rpn: fix = operator registry mapping to AssignRightPaul Buetow
= was registered as AssignLeft (pops name then value) which has reversed stack semantics for the convention name value =. Changed to AssignRight (pops value then name) to match. This is latent dead code (parser intercepts = before reaching executeOperator) but fixing it prevents future confusion. Also corrected misleading comments in operations_variables.go to reflect correct operator-function mappings.
2026-05-24rpn: use named sentinel for invalid parseCategory resultPaul Buetow
Define const invalidCategory Category = -1 so parseCategory returns a clearly-invalid sentinel instead of Category(0)/Universal on unknown names. This prevents silent misclassification if a callers ignores the bool return value. Adds tests for parseCategory with known categories, unknown names, empty string, and sentinel validity.
2026-05-24docs: add metrics.md — metrics system referencePaul Buetow
Complete documentation of all 7 metric categories with units, suffix notation, unit conversion, metric-aware arithmetic rules, cross-category inference, SI/IEC prefix modes, custom metrics, and practical use cases (bandwidth, travel, weight).
2026-05-24docs: add constants.md — built-in constants referencePaul Buetow
Complete documentation of all 36 built-in mathematical constants: fundamental (pi, e, phi, tau), square roots, logarithms, reciprocals, special values (inf, nan), usage examples, practical use cases (geometry, engineering, information theory), and edge cases.
2026-05-24docs: add variables.md; tests: comprehensive assignment operator testsPaul Buetow
- Document all assignment operators (:=, =:, =) with syntax table - Document variable management commands (vars, clear, d) - Document variable lifecycle and practical use cases - Add table-driven tests for := and =: operators - Add tests for = with expression continuation - Add tests for variable reuse in expressions - Add tests for chained assignments - Add tests for vars/clear commands - Add tests for d (delete) operator - Add tests for variable persistence across expressions
2026-05-24docs: add stack operations documentationPaul Buetow
Document dup, swap, pop, show/showstack/print, and clear with: - Stack visualizations for each operation - CLI examples with expected output - Practical use cases (self-comparison, reordering, debugging) - Error handling behavior - Combined examples showing multi-operator workflows
2026-05-24docs: add log operators documentation (lg, log, ln)Paul Buetow
Cover lg (log base 2), log (log base 10), and ln (natural log) with: - Mathematical explanations - RPN examples from live testing - Edge cases (zero and negative number errors) - Practical use cases (information theory, decibels, pH, growth) - Metric handling notes - Implementation references
2026-05-24docs: add hyper operators documentationPaul Buetow
Cover all nine hyper operators ([+], [*], [-], [/], [^], [%], [lg], [log], [ln]) with examples, metric behavior, practical use cases, and edge cases.
2026-05-24docs: add comparison operators documentationPaul Buetow
Cover all 6 comparison operators (gt/>/lt</gte>=/lte<=/eq==/neq!=), truth tables, metric-aware comparisons, boolean coercion in arithmetic, and practical use cases (threshold checks, range validation, guards).
2026-05-24docs: add fast-power.md documenting ** operatorPaul Buetow
Cover binary exponentiation algorithm, syntax, examples, ** vs ^ comparison, edge cases, and performance notes.
2026-05-24docs: add basic arithmetic RPN operators referencePaul Buetow
Document all six basic arithmetic operators (+, -, *, /, ^, %) with: - RPN explanation and stack visualization - Each operator with examples and results - Multi-operand and nested expression examples - Practical use cases (geometry, compound calculations) - Edge cases (division/modulo by zero, negative results)
2026-05-24docs: add percentage calculations guidePaul Buetow
Test all three percentage forms (X% of Y, X is what% of Y, X is Y% of what) and document syntax, examples, practical use cases, edge cases, output format, and known limitations.
2026-05-24rpn: remove duplicate metricRegistry, use ops.MetricRegistry()Paul Buetow
2026-05-24rpn: replace panic with error returns in metric registry lookupsPaul Buetow
resolveMetric, coolMetric, and baseMetric all panicked when the metric registry was missing expected entries. panic() violates Go best practice (no panic except truly unrecoverable) and can crash the REPL. resolveMetric is called for every arithmetic operation, making this a high-impact surface. Change all three functions to return (*Metric, error) instead of panicking. Propagate errors through all callers: - operations_metric.go: resolveMetric, coolMetric, baseMetric, convertToBase, convertFromBase, resultMetricForMul, resultMetricForDiv, Convert - operations_arithmetic.go: binaryMetricOp, Divide, Modulo - operations_compare.go: compareValues - operations_hyper.go: HyperAdd, HyperMultiply, HyperSubtract, HyperDivide, HyperPower, HyperModulo, hyperLog - operations_metric_cmd.go: MetricCompatible
2026-05-24fix: restore RPN stack on ParseAndEvaluate error in RPNHandlerPaul Buetow
RPNHandler.Handle silently swallowed ParseAndEvaluate errors on multi-word input, leaving the persistent REPL stack in a corrupted state. evaluate() modifies r.currentStack directly during token processing, so partial evaluation on failure left orphan values. Fix: save the stack before ParseAndEvaluate, restore it on error. Applied to both the bare RPN path and the rpn/calc prefix path. Added tests: - TestRPNHandlerStackNotCorruptedOnError (bare multi-word input) - TestRPNHandlerErrorReturnedNotSwallowed (error propagation) - TestRPNHandlerStackNotCorruptedOnPrefixedError (rpn/calc prefix)
2026-05-24fix: replace fragile string matching in ResultStack with registry checkPaul Buetow
ResultStack used strings.Contains(err.Error(), "unknown token") to decide whether to fall through to variable lookup. This is fragile and breaks if error messages change. Use the same pattern as handleOperator: check IsStandardOperator/IsHyperOperator directly.
2026-05-24docs: fix metric output examples to match actual behaviorPaul Buetow
- Remove metric suffixes from numeric output (e.g. 1Gbps → 1) Numbers display as plain values; metric info is separate - Fix SI/IEC section to show REPL-only usage with proper format - Fix metric command outputs (factor: 1e+06, sorted category list) - Fix custom metric examples (define inline before use) - Fix cross-category arithmetic output values
2026-05-24refactor(rpn): move dead Value type to test filePaul Buetow
Value struct and its methods (NewNumberValue, NewBoolValue, IsBool, IsNumber, Bool, Float64, Number, String) are defined in number.go but never called in production code — only used in number_value_test.go. Move the Value type and all its methods into the test file so they don't pollute the production package.