summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-24 10:25:32 +0300
committerPaul Buetow <paul@buetow.org>2026-05-24 10:25:32 +0300
commitf8147b44f2888ac150659dced719813f0641fc3c (patch)
tree65019fb70a273c6acc91b001207df406fd987fd1 /internal
parentff836d0d287ef1378fb36f68037e7725a661cf7a (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.go66
-rw-r--r--internal/rpn/operations_compare.go15
-rw-r--r--internal/rpn/operations_hyper.go53
-rw-r--r--internal/rpn/operations_metric.go73
-rw-r--r--internal/rpn/operations_metric_cmd.go10
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)