summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-22 11:03:36 +0300
committerPaul Buetow <paul@buetow.org>2026-05-22 11:03:36 +0300
commit7864ef543aa98e60467eec29c5f49eda8b4f88cf (patch)
tree1e918694bc8c5bf1e9fce84f462d2c4b3dc037ad
parentbc6dccd68b055eb38260cca3fcb716c5e29e7671 (diff)
refactor(rpn): pass MetricRegistry to metric helper functions
Fix DIP violation: resolveMetric, compatibleMetric, resultMetricForMul, and resultMetricForDiv now accept *MetricRegistry parameter instead of calling GetMetricRegistry() globally. Operations passes o.metricRegistry to all helper functions.
-rw-r--r--internal/rpn/operations.go20
-rw-r--r--internal/rpn/operations_metric.go71
2 files changed, 42 insertions, 49 deletions
diff --git a/internal/rpn/operations.go b/internal/rpn/operations.go
index 09955c1..fdb28cd 100644
--- a/internal/rpn/operations.go
+++ b/internal/rpn/operations.go
@@ -349,7 +349,7 @@ func (o *Operations) Add(stack *Stack) error {
return err
}
- aM, bM := resolveMetric(a), resolveMetric(b)
+ aM, bM := resolveMetric(o.metricRegistry, a), resolveMetric(o.metricRegistry, b)
if !categoriesCompatible(aM, bM) {
return metricError("+", aM, bM)
}
@@ -363,7 +363,7 @@ func (o *Operations) Add(stack *Stack) error {
if err != nil {
return buildError("addition", err)
}
- resultMetric := compatibleMetric(aM, bM)
+ resultMetric := compatibleMetric(o.metricRegistry, aM, bM)
resultVal := convertFromBase(aBase+bBase, resultMetric, SI)
stack.Push(NewNumber(resultVal, o.GetMode(), resultMetric))
@@ -377,7 +377,7 @@ func (o *Operations) Subtract(stack *Stack) error {
return err
}
- aM, bM := resolveMetric(a), resolveMetric(b)
+ aM, bM := resolveMetric(o.metricRegistry, a), resolveMetric(o.metricRegistry, b)
if !categoriesCompatible(aM, bM) {
return metricError("-", aM, bM)
}
@@ -390,7 +390,7 @@ func (o *Operations) Subtract(stack *Stack) error {
if err != nil {
return buildError("subtraction", err)
}
- resultMetric := compatibleMetric(aM, bM)
+ resultMetric := compatibleMetric(o.metricRegistry, aM, bM)
resultVal := convertFromBase(aBase-bBase, resultMetric, SI)
stack.Push(NewNumber(resultVal, o.GetMode(), resultMetric))
@@ -404,7 +404,7 @@ func (o *Operations) Multiply(stack *Stack) error {
return err
}
- aM, bM := resolveMetric(a), resolveMetric(b)
+ aM, bM := resolveMetric(o.metricRegistry, a), resolveMetric(o.metricRegistry, b)
// Convert both to base units, multiply, convert back to result metric
aBase, err := convertToBase(a, SI)
@@ -415,7 +415,7 @@ func (o *Operations) Multiply(stack *Stack) error {
if err != nil {
return buildError("multiplication", err)
}
- resultMetric := resultMetricForMul(aM, bM)
+ resultMetric := resultMetricForMul(o.metricRegistry, aM, bM)
resultVal := convertFromBase(aBase*bBase, resultMetric, SI)
stack.Push(NewNumber(resultVal, o.GetMode(), resultMetric))
@@ -438,7 +438,7 @@ func (o *Operations) Divide(stack *Stack) error {
return err
}
- aM, bM := resolveMetric(a), resolveMetric(b)
+ aM, bM := resolveMetric(o.metricRegistry, a), resolveMetric(o.metricRegistry, b)
aBase, err := convertToBase(a, SI)
if err != nil {
@@ -448,7 +448,7 @@ func (o *Operations) Divide(stack *Stack) error {
if err != nil {
return buildError("division", err)
}
- resultMetric := resultMetricForDiv(aM, bM)
+ resultMetric := resultMetricForDiv(o.metricRegistry, aM, bM)
resultVal := convertFromBase(aBase/bBase, resultMetric, SI)
stack.Push(NewNumber(resultVal, o.GetMode(), resultMetric))
@@ -494,7 +494,7 @@ func (o *Operations) Modulo(stack *Stack) error {
return buildError("%", fmt.Errorf("modulo by zero"))
}
- aM, bM := resolveMetric(a), resolveMetric(b)
+ aM, bM := resolveMetric(o.metricRegistry, a), resolveMetric(o.metricRegistry, b)
if !categoriesCompatible(aM, bM) {
return metricError("%", aM, bM)
}
@@ -507,7 +507,7 @@ func (o *Operations) Modulo(stack *Stack) error {
if err != nil {
return buildError("modulo", err)
}
- resultMetric := compatibleMetric(aM, bM)
+ resultMetric := compatibleMetric(o.metricRegistry, aM, bM)
resultVal := convertFromBase(math.Mod(aBase, bBase), resultMetric, SI)
stack.Push(NewNumber(resultVal, o.GetMode(), resultMetric))
diff --git a/internal/rpn/operations_metric.go b/internal/rpn/operations_metric.go
index 9bcdd4b..7a2fd7b 100644
--- a/internal/rpn/operations_metric.go
+++ b/internal/rpn/operations_metric.go
@@ -6,10 +6,10 @@ package rpn
import "fmt"
// resolveMetric returns the metric for a Number, defaulting to Cool if nil.
-func resolveMetric(n Number) *Metric {
+func resolveMetric(reg *MetricRegistry, n Number) *Metric {
m := n.Metric()
if m == nil {
- return GetCoolMetric()
+ return coolMetric(reg)
}
return m
}
@@ -29,12 +29,12 @@ func categoriesCompatible(a, b *Metric) bool {
// compatibleMetric returns the resulting metric for + and - operations.
// Cool absorbs: if either is Cool, result is the other's metric (or Cool if both).
// Same category: result uses left operand's metric.
-func compatibleMetric(a, b *Metric) *Metric {
+func compatibleMetric(reg *MetricRegistry, a, b *Metric) *Metric {
if a == nil {
- a = GetCoolMetric()
+ a = coolMetric(reg)
}
if b == nil {
- b = GetCoolMetric()
+ b = coolMetric(reg)
}
if a.Category == Universal && b.Category == Universal {
return a // Cool
@@ -52,7 +52,7 @@ func compatibleMetric(a, b *Metric) *Metric {
// convertToBase converts a Number's value to its metric's base unit.
// Returns the converted float64 value.
func convertToBase(n Number, mode PrefixMode) (float64, error) {
- m := resolveMetric(n)
+ m := resolveMetric(GetMetricRegistry(), n)
val, err := n.Float64()
if err != nil {
return 0, fmt.Errorf("convertToBase: %w", err)
@@ -63,23 +63,16 @@ func convertToBase(n Number, mode PrefixMode) (float64, error) {
// convertFromBase converts a base-unit value back to the given metric.
func convertFromBase(baseVal float64, m *Metric, mode PrefixMode) float64 {
if m == nil {
- m = GetCoolMetric()
+ m = coolMetric(GetMetricRegistry())
}
return baseVal / m.Factor(mode)
}
// resultMetricForMul computes the resulting metric for multiplication.
-// Cross-category inference rules:
-// - DataRate × Time → DataSize
-// - Time × DataRate → DataSize
-// - Speed × Time → Distance
-// - Time × Speed → Distance
-// - Universal × X → X (Cool absorbs)
-// - Otherwise → Cool (result is unitless product)
-func resultMetricForMul(a, b *Metric) *Metric {
+func resultMetricForMul(reg *MetricRegistry, a, b *Metric) *Metric {
if a == nil || a.Category == Universal {
if b == nil {
- return GetCoolMetric()
+ return coolMetric(reg)
}
return b
}
@@ -90,34 +83,26 @@ func resultMetricForMul(a, b *Metric) *Metric {
// Cross-category inference
switch {
case a.Category == DataRate && b.Category == Time:
- return findBaseMetric("bits")
+ return baseMetric(reg, "bits")
case a.Category == Time && b.Category == DataRate:
- return findBaseMetric("bits")
+ return baseMetric(reg, "bits")
case a.Category == Speed && b.Category == Time:
- return findBaseMetric("m")
+ return baseMetric(reg, "m")
case a.Category == Time && b.Category == Speed:
- return findBaseMetric("m")
+ return baseMetric(reg, "m")
default:
- return GetCoolMetric()
+ return coolMetric(reg)
}
}
// resultMetricForDiv computes the resulting metric for division.
-// Cross-category inference rules:
-// - DataSize / Time → DataRate (base unit)
-// - Distance / Time → Speed (base unit)
-// - DataRate / DataRate → Cool (ratio)
-// - Speed / Speed → Cool (ratio)
-// - Universal / X → X
-// - X / Universal → X
-// - Otherwise → Cool
-func resultMetricForDiv(a, b *Metric) *Metric {
+func resultMetricForDiv(reg *MetricRegistry, a, b *Metric) *Metric {
if a == nil && b == nil {
- return GetCoolMetric()
+ return coolMetric(reg)
}
if b == nil || b.Category == Universal {
if a == nil {
- return GetCoolMetric()
+ return coolMetric(reg)
}
return a
}
@@ -128,11 +113,11 @@ func resultMetricForDiv(a, b *Metric) *Metric {
// Cross-category inference
switch {
case a.Category == DataSize && b.Category == Time:
- return findBaseMetric("bps")
+ return baseMetric(reg, "bps")
case a.Category == Distance && b.Category == Time:
- return findBaseMetric("mps")
+ return baseMetric(reg, "mps")
default:
- return GetCoolMetric()
+ return coolMetric(reg)
}
}
@@ -150,10 +135,18 @@ func metricError(op string, a, b *Metric) error {
op, aName, aCat, bName, bCat)
}
-// findBaseMetric looks up a base metric from the global registry.
-// Panics if not found (indicates misconfiguration).
-func findBaseMetric(name string) *Metric {
- m, ok := GetMetricRegistry().Find(name)
+// coolMetric returns the Cool metric from the registry.
+func coolMetric(reg *MetricRegistry) *Metric {
+ m, ok := reg.Find("Cool")
+ if !ok {
+ panic("metric registry missing Cool metric")
+ }
+ return m
+}
+
+// baseMetric looks up a base metric from the registry.
+func baseMetric(reg *MetricRegistry, name string) *Metric {
+ m, ok := reg.Find(name)
if !ok {
panic(fmt.Sprintf("metric registry missing base unit %q", name))
}