format/mutator.go

126 lines
2.6 KiB
Go
Raw Permalink Normal View History

2022-04-18 17:39:41 +02:00
package main
2022-05-22 21:06:06 +02:00
import (
"reflect"
"strconv"
)
2022-04-18 17:39:41 +02:00
type NumMutator struct {
Op NumOperator
Var bool
Value int
}
type NumOperator string
const (
NumOperatorAdd NumOperator = "+"
NumOperatorSub NumOperator = "-"
NumOperatorMul NumOperator = "*"
NumOperatorDiv NumOperator = "/"
)
func NewNumOperatorFromString(str string) NumOperator {
switch str {
case "+":
return NumOperatorAdd
case "-":
return NumOperatorSub
case "*":
return NumOperatorMul
case "/":
return NumOperatorDiv
default:
panic("invalid number operator: " + str)
}
}
2022-05-22 21:06:06 +02:00
var numMutatorCache = map[string]interface{}{}
func numMut2func[T int64 | float64](mutation string) (f func(value T, vars []string) T) {
if mutation == "" {
return func(value T, vars []string) T { return value }
}
// caching
if v, ok := numMutatorCache[mutation]; ok {
return v.(func(value T, vars []string) T)
}
defer func() { numMutatorCache[mutation] = f }()
matches := numMutationPattern.FindAllStringSubmatch(mutation, -1)
mutators := make([]NumMutator, 0, len(matches))
var err error
for _, match := range matches {
mut := NumMutator{Op: NewNumOperatorFromString(match[1])}
if match[3] == "" {
mut.Value, err = strconv.Atoi(match[2])
mut.Var = false
if err != nil {
panic("invalid number in number mutator: " + match[2])
}
} else {
mut.Var = true
mut.Value, err = strconv.Atoi(match[3])
if err != nil {
panic("invalid back reference group in number mutator: " + match[2])
}
}
mutators = append(mutators, mut)
}
numberParser := number_parser[T]()
return func(value T, vars []string) T {
for _, mutator := range mutators {
var otherValue T
if mutator.Var {
other := numberParser(vars[mutator.Value])
otherValue = T(other)
} else {
otherValue = T(mutator.Value)
}
switch mutator.Op {
case NumOperatorAdd:
value += otherValue
case NumOperatorSub:
value -= otherValue
case NumOperatorMul:
value *= otherValue
case NumOperatorDiv:
value /= otherValue
default:
}
}
return value
}
}
func number_parser[T int64 | float64]() func(str string) T {
typeOfT := reflect.TypeOf(new(T)).Elem()
typeOfInt64 := reflect.TypeOf(new(int64)).Elem()
typeOfFloat64 := reflect.TypeOf(new(float64)).Elem()
if typeOfT == typeOfInt64 {
return func(str string) T {
num, err := strconv.Atoi(str)
if err != nil {
panic("expected integer but found " + str)
}
return T(num)
}
} else if typeOfT == typeOfFloat64 {
return func(str string) T {
num, err := strconv.ParseFloat(str, 64)
if err != nil {
panic("expected float but found " + str)
}
return T(num)
}
}
panic("invalid number type")
}