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")
|
|
|
|
}
|