summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-24 18:41:37 +0300
committerPaul Buetow <paul@buetow.org>2026-05-24 18:41:37 +0300
commitf3cfcb0a95c6ce1662f0cb2c4eae10b3bf37a7bd (patch)
treecc87ff8bff5306c7fc7d4d90871d327173e179a5
parent1b31b4a6bbf4b9ae371801d631c8eb692f17efb6 (diff)
fix(rpn): complete DIP for MetricRegistry — use MetricReader/MetricWriter interfaces (task ek)
-rw-r--r--internal/rpn/metric_registry.go8
-rw-r--r--internal/rpn/operations.go6
-rw-r--r--internal/rpn/operations_arithmetic.go6
-rw-r--r--internal/rpn/operations_metric.go16
-rw-r--r--internal/rpn/operations_metric_cmd.go12
-rw-r--r--internal/rpn/rpn_state.go2
6 files changed, 33 insertions, 17 deletions
diff --git a/internal/rpn/metric_registry.go b/internal/rpn/metric_registry.go
index a221e56..1aed264 100644
--- a/internal/rpn/metric_registry.go
+++ b/internal/rpn/metric_registry.go
@@ -28,6 +28,14 @@ type MetricReader interface {
ListByCategory(cat Category) []*Metric
}
+// MetricWriter defines the write operations on a metric registry.
+type MetricWriter interface {
+ Register(m *Metric)
+ RegisterAlias(alias, canonicalName string)
+ Unregister(name string) error
+ MarkExactMatch(names ...string)
+}
+
// Global registry instance.
var defaultRegistry *MetricRegistry
var registryOnce sync.Once
diff --git a/internal/rpn/operations.go b/internal/rpn/operations.go
index f79457c..989ce87 100644
--- a/internal/rpn/operations.go
+++ b/internal/rpn/operations.go
@@ -13,7 +13,7 @@ type Operations struct {
consts ConstantsProvider
mode CalculationMode
prefixMode PrefixMode
- metricRegistry *MetricRegistry
+ metricRegistry MetricReader
mu sync.RWMutex
}
@@ -38,8 +38,8 @@ var (
// NewOperations creates a new Operations instance with the given variable store.
// Does not create a ConstantsProvider internally; caller must use SetConstants.
// If no registry is provided, defaults to the global MetricRegistry.
-func NewOperations(vars VariableStore, reg ...*MetricRegistry) *Operations {
- r := GetMetricRegistry()
+func NewOperations(vars VariableStore, reg ...MetricReader) *Operations {
+ r := MetricReader(GetMetricRegistry())
if len(reg) > 0 && reg[0] != nil {
r = reg[0]
}
diff --git a/internal/rpn/operations_arithmetic.go b/internal/rpn/operations_arithmetic.go
index 1c4252c..4468edb 100644
--- a/internal/rpn/operations_arithmetic.go
+++ b/internal/rpn/operations_arithmetic.go
@@ -24,7 +24,7 @@ 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(MetricReader, *Metric, *Metric) (*Metric, error),
preChecks ...func(StackValue) error,
) error {
a, b, err := popTwo(stack, op)
@@ -85,7 +85,7 @@ func (o *Operations) Add(stack *Stack) error {
return nil
},
func(a, b float64) float64 { return a + b },
- func(reg *MetricRegistry, aM, bM *Metric) (*Metric, error) {
+ func(reg MetricReader, aM, bM *Metric) (*Metric, error) {
return compatibleMetric(reg, aM, bM), nil
},
)
@@ -102,7 +102,7 @@ func (o *Operations) Subtract(stack *Stack) error {
return nil
},
func(a, b float64) float64 { return a - b },
- func(reg *MetricRegistry, aM, bM *Metric) (*Metric, error) {
+ func(reg MetricReader, aM, bM *Metric) (*Metric, error) {
return compatibleMetric(reg, aM, bM), nil
},
)
diff --git a/internal/rpn/operations_metric.go b/internal/rpn/operations_metric.go
index 64cce98..b621085 100644
--- a/internal/rpn/operations_metric.go
+++ b/internal/rpn/operations_metric.go
@@ -6,7 +6,7 @@ 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 MetricReader, n StackValue) (*Metric, error) {
m := n.Metric()
if m == nil {
var ok bool
@@ -59,7 +59,7 @@ func categoriesCompatible(a, b *Metric) bool {
}
// compatibleMetric returns the resulting metric for + and - operations.
-func compatibleMetric(reg *MetricRegistry, a, b *Metric) *Metric {
+func compatibleMetric(reg MetricReader, a, b *Metric) *Metric {
return resultMetricForAdd([]*Metric{a, b})
}
@@ -73,7 +73,7 @@ func compatibleMetric(reg *MetricRegistry, a, b *Metric) *Metric {
// NOT by 1 (base units).
//
// Returns the converted float64 value in base units.
-func convertToBase(reg *MetricRegistry, n StackValue, mode PrefixMode, resultMetric *Metric) (float64, error) {
+func convertToBase(reg MetricReader, n StackValue, mode PrefixMode, resultMetric *Metric) (float64, error) {
nv, ok := n.(NumericValue)
if !ok {
return 0, fmt.Errorf("convertToBase: value %q is not numeric", n)
@@ -95,7 +95,7 @@ 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 MetricReader, baseVal float64, m *Metric, mode PrefixMode) (float64, error) {
if m == nil {
var err error
m, err = baseMetric(reg, "Cool")
@@ -116,7 +116,7 @@ var multiplicationInference = map[[2]Category]string{
}
// resultMetricForMul computes the resulting metric for multiplication.
-func resultMetricForMul(reg *MetricRegistry, a, b *Metric) (*Metric, error) {
+func resultMetricForMul(reg MetricReader, a, b *Metric) (*Metric, error) {
if a == nil || a.Category == Universal {
if b == nil {
return baseMetric(reg, "Cool")
@@ -141,7 +141,7 @@ var divisionInference = map[[2]Category]string{
}
// resultMetricForDiv computes the resulting metric for division.
-func resultMetricForDiv(reg *MetricRegistry, a, b *Metric) (*Metric, error) {
+func resultMetricForDiv(reg MetricReader, a, b *Metric) (*Metric, error) {
if a == nil && b == nil {
return baseMetric(reg, "Cool")
}
@@ -179,7 +179,7 @@ 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 MetricReader) (*Metric, error) {
m, ok := reg.Find("Cool")
if !ok {
return nil, fmt.Errorf("metric registry missing Cool metric")
@@ -188,7 +188,7 @@ func coolMetric(reg *MetricRegistry) (*Metric, error) {
}
// baseMetric looks up a base metric from the registry.
-func baseMetric(reg *MetricRegistry, name string) (*Metric, error) {
+func baseMetric(reg MetricReader, name string) (*Metric, error) {
m, ok := reg.Find(name)
if !ok {
return nil, fmt.Errorf("metric registry missing base unit %q", name)
diff --git a/internal/rpn/operations_metric_cmd.go b/internal/rpn/operations_metric_cmd.go
index 255daed..370be98 100644
--- a/internal/rpn/operations_metric_cmd.go
+++ b/internal/rpn/operations_metric_cmd.go
@@ -150,11 +150,19 @@ func (o *Operations) CustomDefine(name string, factor float64, category string)
Factor: func(PrefixMode) float64 { return factor },
IsCustom: true,
}
- reg.Register(m)
+ writer, ok := reg.(MetricWriter)
+ if !ok {
+ return fmt.Errorf("metric registry does not support write operations")
+ }
+ writer.Register(m)
return nil
}
// CustomUndefine removes a custom metric.
func (o *Operations) CustomUndefine(name string) error {
- return o.metricRegistry.Unregister(name)
+ writer, ok := o.metricRegistry.(MetricWriter)
+ if !ok {
+ return fmt.Errorf("metric registry does not support write operations")
+ }
+ return writer.Unregister(name)
}
diff --git a/internal/rpn/rpn_state.go b/internal/rpn/rpn_state.go
index adcf449..a42372a 100644
--- a/internal/rpn/rpn_state.go
+++ b/internal/rpn/rpn_state.go
@@ -22,7 +22,7 @@ type RPN struct {
// NewRPN creates a new RPN parser and evaluator with the given variable store.
// If no registry is provided, defaults to the global MetricRegistry.
-func NewRPN(vars VariableStore, reg ...*MetricRegistry) *RPN {
+func NewRPN(vars VariableStore, reg ...MetricReader) *RPN {
consts := NewConstants()
ops := NewOperations(vars, reg...)
ops.SetMode(FloatMode) // Set default mode