diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-24 10:25:32 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-24 10:25:32 +0300 |
| commit | f8147b44f2888ac150659dced719813f0641fc3c (patch) | |
| tree | 65019fb70a273c6acc91b001207df406fd987fd1 /internal | |
| parent | ff836d0d287ef1378fb36f68037e7725a661cf7a (diff) | |
rpn: revert resolveMetric and helpers to single return values
resolveMetric was changed from returning *Metric to (*Metric, error) but
the error path only fires when Cool is missing from the registry, which
is an impossible runtime state (previously a panic). This added ~90 lines
of boilerplate error handling across multiple files.
Revert resolveMetric, coolMetric, baseMetric, resultMetricForMul,
resultMetricForDiv, compatibleMetric, and convertFromBase to single
return values that panic on impossible states. convertToBase retains
its error return for reachable error cases (type assertion, Float64).
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/rpn/operations_arithmetic.go | 66 | ||||
| -rw-r--r-- | internal/rpn/operations_compare.go | 15 | ||||
| -rw-r--r-- | internal/rpn/operations_hyper.go | 53 | ||||
| -rw-r--r-- | internal/rpn/operations_metric.go | 73 | ||||
| -rw-r--r-- | internal/rpn/operations_metric_cmd.go | 10 |
5 files changed, 62 insertions, 155 deletions
diff --git a/internal/rpn/operations_arithmetic.go b/internal/rpn/operations_arithmetic.go index d243140..ca32721 100644 --- a/internal/rpn/operations_arithmetic.go +++ b/internal/rpn/operations_arithmetic.go @@ -22,21 +22,15 @@ func (o *Operations) binaryMetricOp( op string, compatCheck func(*Metric, *Metric) error, compute func(float64, float64) float64, - resultMetricFn func(*MetricRegistry, *Metric, *Metric) (*Metric, error), + resultMetricFn func(*MetricRegistry, *Metric, *Metric) *Metric, ) error { a, b, err := popTwo(stack, op) if err != nil { return err } - aM, err := resolveMetric(o.metricRegistry, a) - if err != nil { - return buildError(op, err) - } - bM, err := resolveMetric(o.metricRegistry, b) - if err != nil { - return buildError(op, err) - } + aM := resolveMetric(o.metricRegistry, a) + bM := resolveMetric(o.metricRegistry, b) if compatCheck != nil { if err := compatCheck(aM, bM); err != nil { return err @@ -44,10 +38,7 @@ func (o *Operations) binaryMetricOp( } pm := o.GetPrefixMode() - resultMetric, err := resultMetricFn(o.metricRegistry, aM, bM) - if err != nil { - return buildError(op, err) - } + resultMetric := resultMetricFn(o.metricRegistry, aM, bM) aBase, err := convertToBase(o.metricRegistry, a, pm, resultMetric) if err != nil { return buildError(op, err) @@ -56,10 +47,7 @@ func (o *Operations) binaryMetricOp( if err != nil { return buildError(op, err) } - resultVal, err := convertFromBase(o.metricRegistry, compute(aBase, bBase), resultMetric, pm) - if err != nil { - return buildError(op, err) - } + resultVal := convertFromBase(o.metricRegistry, compute(aBase, bBase), resultMetric, pm) stack.Push(NewNumberWithMetric(resultVal, o.GetMode(), resultMetric)) return nil @@ -120,32 +108,20 @@ func (o *Operations) Divide(stack *Stack) error { return buildError("/", fmt.Errorf("division by zero")) } - aM, err := resolveMetric(o.metricRegistry, a) - if err != nil { - return buildError("/", err) - } - bM, err := resolveMetric(o.metricRegistry, b) - if err != nil { - return buildError("/", err) - } + aM := resolveMetric(o.metricRegistry, a) + bM := resolveMetric(o.metricRegistry, b) pm := o.GetPrefixMode() - resultMetric, err := resultMetricForDiv(o.metricRegistry, aM, bM) - if err != nil { - return buildError("/", err) - } + resultMetric := resultMetricForDiv(o.metricRegistry, aM, bM) aBase, err := convertToBase(o.metricRegistry, a, pm, resultMetric) if err != nil { - return buildError("division", err) + return buildError("/", err) } bBase, err := convertToBase(o.metricRegistry, b, pm, resultMetric) if err != nil { - return buildError("division", err) - } - resultVal, err := convertFromBase(o.metricRegistry, aBase/bBase, resultMetric, pm) - if err != nil { return buildError("/", err) } + resultVal := convertFromBase(o.metricRegistry, aBase/bBase, resultMetric, pm) stack.Push(NewNumberWithMetric(resultVal, o.GetMode(), resultMetric)) return nil @@ -196,35 +172,23 @@ func (o *Operations) Modulo(stack *Stack) error { return buildError("%", fmt.Errorf("modulo by zero")) } - aM, err := resolveMetric(o.metricRegistry, a) - if err != nil { - return buildError("%", err) - } - bM, err := resolveMetric(o.metricRegistry, b) - if err != nil { - return buildError("%", err) - } + aM := resolveMetric(o.metricRegistry, a) + bM := resolveMetric(o.metricRegistry, b) if !categoriesCompatible(aM, bM) { return metricError("%", aM, bM) } pm := o.GetPrefixMode() - resultMetric, err := compatibleMetric(o.metricRegistry, aM, bM) - if err != nil { - return buildError("%", err) - } + resultMetric := compatibleMetric(o.metricRegistry, aM, bM) aBase, err := convertToBase(o.metricRegistry, a, pm, resultMetric) if err != nil { - return buildError("modulo", err) + return buildError("%", err) } bBase, err := convertToBase(o.metricRegistry, b, pm, resultMetric) if err != nil { - return buildError("modulo", err) - } - resultVal, err := convertFromBase(o.metricRegistry, math.Mod(aBase, bBase), resultMetric, pm) - if err != nil { return buildError("%", err) } + resultVal := convertFromBase(o.metricRegistry, math.Mod(aBase, bBase), resultMetric, pm) stack.Push(NewNumberWithMetric(resultVal, o.GetMode(), resultMetric)) return nil diff --git a/internal/rpn/operations_compare.go b/internal/rpn/operations_compare.go index 5ab4a56..81192e9 100644 --- a/internal/rpn/operations_compare.go +++ b/internal/rpn/operations_compare.go @@ -13,23 +13,14 @@ func compareValues(o *Operations, stack *Stack, op string, cmp func(float64, flo return err } - aM, err := resolveMetric(o.metricRegistry, a) - if err != nil { - return buildError(op, err) - } - bM, err := resolveMetric(o.metricRegistry, b) - if err != nil { - return buildError(op, err) - } + aM := resolveMetric(o.metricRegistry, a) + bM := resolveMetric(o.metricRegistry, b) if !categoriesCompatible(aM, bM) { return metricError(op, aM, bM) } pm := o.GetPrefixMode() - resultMetric, err := compatibleMetric(o.metricRegistry, aM, bM) - if err != nil { - return buildError(op, err) - } + resultMetric := compatibleMetric(o.metricRegistry, aM, bM) aBase, err := convertToBase(o.metricRegistry, a, pm, resultMetric) if err != nil { return buildError(op, err) diff --git a/internal/rpn/operations_hyper.go b/internal/rpn/operations_hyper.go index 34b520b..19c9398 100644 --- a/internal/rpn/operations_hyper.go +++ b/internal/rpn/operations_hyper.go @@ -23,11 +23,7 @@ func (o *Operations) HyperAdd(stack *Stack) error { // Resolve metrics for all values metrics := make([]*Metric, len(values)) for i, v := range values { - m, err := resolveMetric(o.metricRegistry, v) - if err != nil { - return buildError("[+]", err) - } - metrics[i] = m + metrics[i] = resolveMetric(o.metricRegistry, v) } // Validate all are compatible (all same category, or Cool absorbs) @@ -49,10 +45,7 @@ func (o *Operations) HyperAdd(stack *Stack) error { sum += base } - resultVal, err := convertFromBase(o.metricRegistry, sum, resultMetric, pm) - if err != nil { - return buildError("[+]", err) - } + resultVal := convertFromBase(o.metricRegistry, sum, resultMetric, pm) stack.Push(NewNumberWithMetric(resultVal, o.GetMode(), resultMetric)) return nil @@ -79,10 +72,7 @@ func (o *Operations) HyperMultiply(stack *Stack) error { } } - cool, err := coolMetric(o.metricRegistry) - if err != nil { - return buildError("[*]", err) - } + cool := coolMetric(o.metricRegistry) stack.Push(NewNumberWithMetric(product, o.GetMode(), cool)) return nil } @@ -99,11 +89,7 @@ func (o *Operations) HyperSubtract(stack *Stack) error { // Resolve metrics for all values metrics := make([]*Metric, len(values)) for i, v := range values { - m, err := resolveMetric(o.metricRegistry, v) - if err != nil { - return buildError("[-]", err) - } - metrics[i] = m + metrics[i] = resolveMetric(o.metricRegistry, v) } // Validate all are compatible (all same category, or Cool absorbs) @@ -129,10 +115,7 @@ func (o *Operations) HyperSubtract(stack *Stack) error { result -= base } - resultVal, err := convertFromBase(o.metricRegistry, result, resultMetric, pm) - if err != nil { - return buildError("[-]", err) - } + resultVal := convertFromBase(o.metricRegistry, result, resultMetric, pm) stack.Push(NewNumberWithMetric(resultVal, o.GetMode(), resultMetric)) return nil @@ -162,10 +145,7 @@ func (o *Operations) HyperDivide(stack *Stack) error { result /= val } - cool, err := coolMetric(o.metricRegistry) - if err != nil { - return buildError("[/]", err) - } + cool := coolMetric(o.metricRegistry) stack.Push(NewNumberWithMetric(result, o.GetMode(), cool)) return nil } @@ -191,10 +171,7 @@ func (o *Operations) HyperPower(stack *Stack) error { result = math.Pow(result, val) } - cool, err := coolMetric(o.metricRegistry) - if err != nil { - return buildError("[^]", err) - } + cool := coolMetric(o.metricRegistry) stack.Push(NewNumberWithMetric(result, o.GetMode(), cool)) return nil } @@ -211,11 +188,7 @@ func (o *Operations) HyperModulo(stack *Stack) error { // Resolve metrics for all values metrics := make([]*Metric, len(values)) for i, v := range values { - m, err := resolveMetric(o.metricRegistry, v) - if err != nil { - return buildError("[%]", err) - } - metrics[i] = m + metrics[i] = resolveMetric(o.metricRegistry, v) } // Validate all are compatible (all same category, or Cool absorbs) @@ -244,10 +217,7 @@ func (o *Operations) HyperModulo(stack *Stack) error { result = math.Mod(result, base) } - resultVal, err := convertFromBase(o.metricRegistry, result, resultMetric, pm) - if err != nil { - return buildError("[%]", err) - } + resultVal := convertFromBase(o.metricRegistry, result, resultMetric, pm) stack.Push(NewNumberWithMetric(resultVal, o.GetMode(), resultMetric)) return nil @@ -273,10 +243,7 @@ func (o *Operations) hyperLog(stack *Stack, opName string, logFn func(float64) f result += logFn(val) } - cool, err := coolMetric(o.metricRegistry) - if err != nil { - return buildError(opName, err) - } + cool := coolMetric(o.metricRegistry) stack.Push(NewNumberWithMetric(result, o.GetMode(), cool)) return nil } diff --git a/internal/rpn/operations_metric.go b/internal/rpn/operations_metric.go index 6758429..a9f91cc 100644 --- a/internal/rpn/operations_metric.go +++ b/internal/rpn/operations_metric.go @@ -6,12 +6,16 @@ package rpn import "fmt" // resolveMetric returns the metric for a StackValue, defaulting to Cool if nil. -func resolveMetric(reg *MetricRegistry, n StackValue) (*Metric, error) { +func resolveMetric(reg *MetricRegistry, n StackValue) *Metric { m := n.Metric() if m == nil { - return coolMetric(reg) + var ok bool + m, ok = reg.Find("Cool") + if !ok { + panic("metric registry missing Cool metric") + } } - return m, nil + return m } // validateCategories checks that all metrics belong to the same category. @@ -56,8 +60,8 @@ func categoriesCompatible(a, b *Metric) bool { } // compatibleMetric returns the resulting metric for + and - operations. -func compatibleMetric(reg *MetricRegistry, a, b *Metric) (*Metric, error) { - return resultMetricForAdd([]*Metric{a, b}), nil +func compatibleMetric(reg *MetricRegistry, a, b *Metric) *Metric { + return resultMetricForAdd([]*Metric{a, b}) } // convertToBase converts a StackValue's value to its metric's base unit. @@ -75,10 +79,7 @@ func convertToBase(reg *MetricRegistry, n StackValue, mode PrefixMode, resultMet if !ok { return 0, fmt.Errorf("convertToBase: value %q is not numeric", n) } - m, err := resolveMetric(reg, n) - if err != nil { - return 0, fmt.Errorf("convertToBase: %w", err) - } + m := resolveMetric(reg, n) val, err := nv.Float64() if err != nil { return 0, fmt.Errorf("convertToBase: %w", err) @@ -92,27 +93,23 @@ func convertToBase(reg *MetricRegistry, n StackValue, mode PrefixMode, resultMet } // convertFromBase converts a base-unit value back to the given metric. -func convertFromBase(reg *MetricRegistry, baseVal float64, m *Metric, mode PrefixMode) (float64, error) { +func convertFromBase(reg *MetricRegistry, baseVal float64, m *Metric, mode PrefixMode) float64 { if m == nil { - var err error - m, err = coolMetric(reg) - if err != nil { - return 0, err - } + m = baseMetric(reg, "Cool") } - return baseVal / m.Factor(mode), nil + return baseVal / m.Factor(mode) } // resultMetricForMul computes the resulting metric for multiplication. -func resultMetricForMul(reg *MetricRegistry, a, b *Metric) (*Metric, error) { +func resultMetricForMul(reg *MetricRegistry, a, b *Metric) *Metric { if a == nil || a.Category == Universal { if b == nil { - return coolMetric(reg) + return baseMetric(reg, "Cool") } - return b, nil + return b } if b == nil || b.Category == Universal { - return a, nil + return a } // Cross-category inference @@ -126,26 +123,26 @@ func resultMetricForMul(reg *MetricRegistry, a, b *Metric) (*Metric, error) { case a.Category == Time && b.Category == Speed: return baseMetric(reg, "m") default: - return coolMetric(reg) + return baseMetric(reg, "Cool") } } // resultMetricForDiv computes the resulting metric for division. -func resultMetricForDiv(reg *MetricRegistry, a, b *Metric) (*Metric, error) { +func resultMetricForDiv(reg *MetricRegistry, a, b *Metric) *Metric { if a == nil && b == nil { - return coolMetric(reg) + return baseMetric(reg, "Cool") } if b == nil || b.Category == Universal { if a == nil { - return coolMetric(reg) + return baseMetric(reg, "Cool") } - return a, nil + return a } // When dividend is Cool (unitless) and divisor has a metric, // result should be Cool. E.g., 5 / 10km → 0.5 (Cool, not km). // Cool-absorbing is designed for addition, not division. if a == nil || a.Category == Universal { - return coolMetric(reg) + return baseMetric(reg, "Cool") } // Cross-category inference @@ -155,7 +152,7 @@ func resultMetricForDiv(reg *MetricRegistry, a, b *Metric) (*Metric, error) { case a.Category == Distance && b.Category == Time: return baseMetric(reg, "mps") default: - return coolMetric(reg) + return baseMetric(reg, "Cool") } } @@ -174,21 +171,21 @@ func metricError(op string, a, b *Metric) error { } // coolMetric returns the Cool metric from the registry. -func coolMetric(reg *MetricRegistry) (*Metric, error) { +func coolMetric(reg *MetricRegistry) *Metric { m, ok := reg.Find("Cool") if !ok { - return nil, fmt.Errorf("metric registry missing Cool metric") + panic("metric registry missing Cool metric") } - return m, nil + return m } // baseMetric looks up a base metric from the registry. -func baseMetric(reg *MetricRegistry, name string) (*Metric, error) { +func baseMetric(reg *MetricRegistry, name string) *Metric { m, ok := reg.Find(name) if !ok { - return nil, fmt.Errorf("metric registry missing base unit %q", name) + panic(fmt.Sprintf("metric registry missing base unit %q", name)) } - return m, nil + return m } // Convert converts a value from its current metric to a target metric. @@ -208,10 +205,7 @@ func (o *Operations) Convert(stack *Stack) error { } // 3. Get metrics targetMetric := target.Metric() - valueMetric, err := resolveMetric(o.metricRegistry, value) - if err != nil { - return buildError("convert", err) - } + valueMetric := resolveMetric(o.metricRegistry, value) // 4. Validate same category (or Cool absorbing) if !categoriesCompatible(valueMetric, targetMetric) { return metricError("convert", valueMetric, targetMetric) @@ -222,10 +216,7 @@ func (o *Operations) Convert(stack *Stack) error { if err != nil { return buildError("convert", err) } - resultVal, err := convertFromBase(o.metricRegistry, baseVal, targetMetric, pm) - if err != nil { - return buildError("convert", err) - } + resultVal := convertFromBase(o.metricRegistry, baseVal, targetMetric, pm) // 6. Push result with target metric stack.Push(NewNumberWithMetric(resultVal, o.GetMode(), targetMetric)) return nil diff --git a/internal/rpn/operations_metric_cmd.go b/internal/rpn/operations_metric_cmd.go index 8e0e519..31a2d2f 100644 --- a/internal/rpn/operations_metric_cmd.go +++ b/internal/rpn/operations_metric_cmd.go @@ -66,14 +66,8 @@ func (o *Operations) MetricCompatible(stack *Stack) (string, error) { vals := stack.Values() top := vals[len(vals)-1] second := vals[len(vals)-2] - mA, err := resolveMetric(o.metricRegistry, second) - if err != nil { - return "", buildError("metric compatible", err) - } - mB, err := resolveMetric(o.metricRegistry, top) - if err != nil { - return "", buildError("metric compatible", err) - } + mA := resolveMetric(o.metricRegistry, second) + mB := resolveMetric(o.metricRegistry, top) compatible := categoriesCompatible(mA, mB) result := fmt.Sprintf("%s (%s) and %s (%s): %v", mA.Name, mA.Category, mB.Name, mB.Category, compatible) |
