color support

This commit is contained in:
Timon Ringwald 2022-05-22 21:06:06 +02:00
parent 06648c9177
commit 9bd2401fd9
5 changed files with 159 additions and 98 deletions

36
color.go Normal file
View File

@ -0,0 +1,36 @@
package main
import "github.com/fatih/color"
var colorCache = map[string]*color.Color{}
func makeColor(name string) (c *color.Color) {
// caching
if c, ok := colorCache[name]; ok {
return c
}
defer func() { colorCache[name] = c }()
switch name {
case "black":
return color.New(color.FgBlack)
case "red":
return color.New(color.FgRed)
case "green":
return color.New(color.FgGreen)
case "yellow":
return color.New(color.FgYellow)
case "blue":
return color.New(color.FgBlue)
case "magenta":
return color.New(color.FgMagenta)
case "cyan":
return color.New(color.FgCyan)
case "white":
return color.New(color.FgWhite)
case "":
return color.New()
default:
panic("unknown color name. valid color names: black, red, green, yellow, blue, magenta, cyan, white")
}
}

7
go.mod
View File

@ -1,3 +1,10 @@
module git.tordarus.net/Tordarus/format module git.tordarus.net/Tordarus/format
go 1.18 go 1.18
require (
github.com/fatih/color v1.13.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect
)

14
go.sum Normal file
View File

@ -0,0 +1,14 @@
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e h1:w36l2Uw3dRan1K3TyXriXvY+6T56GNmlKGcqiQUJDfM=
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

106
main.go
View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"reflect"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -46,7 +45,7 @@ var (
// it may be useful to have a boolean flag for this behavior // it may be useful to have a boolean flag for this behavior
lineParseAmount = flag.Int("n", 1, "amount of lines to feed into input pattern") lineParseAmount = flag.Int("n", 1, "amount of lines to feed into input pattern")
replacePattern = regexp.MustCompile(`\{(\d+)(?::(.*?))?(?::(.*?))?\}`) replacePattern = regexp.MustCompile(`\{(\d+)(?::(.*?))?(?::(.*?))?(?::(.*?))?\}`)
numMutationPattern = regexp.MustCompile(`([+\-*/])(\d+|\((\d+)\))`) numMutationPattern = regexp.MustCompile(`([+\-*/])(\d+|\((\d+)\))`)
) )
@ -123,7 +122,8 @@ func replaceVars(format string, vars ...string) string {
for _, replacement := range replacements { for _, replacement := range replacements {
rplStr := replacement[0] rplStr := replacement[0]
varIndex, _ := strconv.Atoi(replacement[1]) varIndex, _ := strconv.Atoi(replacement[1])
rplFmt := replacement[2] rplColor := makeColor(replacement[2])
rplFmt := replacement[3]
// default format if not specified by user // default format if not specified by user
if rplFmt == "" { if rplFmt == "" {
@ -132,106 +132,16 @@ func replaceVars(format string, vars ...string) string {
if strings.HasSuffix(rplFmt, "d") { // replace integers if strings.HasSuffix(rplFmt, "d") { // replace integers
value, _ := strconv.ParseInt(vars[varIndex], 10, 64) value, _ := strconv.ParseInt(vars[varIndex], 10, 64)
mutate := numMut2func[int64](replacement[3]) mutate := numMut2func[int64](replacement[4])
format = strings.Replace(format, rplStr, fmt.Sprintf(rplFmt, mutate(value, vars)), 1) format = strings.Replace(format, rplStr, rplColor.Sprintf(rplFmt, mutate(value, vars)), 1)
} else if strings.HasSuffix(rplFmt, "f") || strings.HasSuffix(rplFmt, "g") { // replace floats } else if strings.HasSuffix(rplFmt, "f") || strings.HasSuffix(rplFmt, "g") { // replace floats
value, _ := strconv.ParseFloat(vars[varIndex], 64) value, _ := strconv.ParseFloat(vars[varIndex], 64)
mutate := numMut2func[float64](replacement[3]) mutate := numMut2func[float64](replacement[4])
format = strings.Replace(format, rplStr, fmt.Sprintf(rplFmt, mutate(value, vars)), 1) format = strings.Replace(format, rplStr, rplColor.Sprintf(rplFmt, mutate(value, vars)), 1)
} else { // replace strings } else { // replace strings
format = strings.Replace(format, rplStr, fmt.Sprintf(rplFmt, vars[varIndex]), 1) format = strings.Replace(format, rplStr, rplColor.Sprintf(rplFmt, vars[varIndex]), 1)
} }
} }
return format return format
} }
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")
}

View File

@ -1,5 +1,10 @@
package main package main
import (
"reflect"
"strconv"
)
type NumMutator struct { type NumMutator struct {
Op NumOperator Op NumOperator
Var bool Var bool
@ -29,3 +34,92 @@ func NewNumOperatorFromString(str string) NumOperator {
panic("invalid number operator: " + str) panic("invalid number operator: " + str)
} }
} }
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")
}