// Copyright (c) 2023 IBM Corp. // All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cli import ( "fmt" "log" "os" "path/filepath" "time" C "github.com/urfave/cli/v2" ) func generateUnsliced(f *os.File, i int) { // Create the optionize version fmt.Fprintf(f, "\n// Unsliced%d converts a function taking a slice parameter into a function with %d parameters\n", i, i) fmt.Fprintf(f, "func Unsliced%d[F ~func([]T) R, T, R any](f F) func(", i) for j := 0; j < i; j++ { if j > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "T") } fmt.Fprintf(f, ") R {\n") fmt.Fprintf(f, " return func(") for j := 0; j < i; j++ { if j > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "t%d", j+1) } if i > 0 { fmt.Fprintf(f, " T") } fmt.Fprintf(f, ") R {\n") fmt.Fprintf(f, " return f([]T{") for j := 0; j < i; j++ { if j > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "t%d", j+1) } fmt.Fprintln(f, "})") fmt.Fprintln(f, " }") fmt.Fprintln(f, "}") } func generateVariadic(f *os.File, i int) { // Create the nullary version fmt.Fprintf(f, "\n// Variadic%d converts a function taking %d parameters and a final slice into a function with %d parameters but a final variadic argument\n", i, i, i) fmt.Fprintf(f, "func Variadic%d[", i) for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "T%d", j) } if i > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "V, R any](f func(") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "T%d", j) } if i > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "[]V) R) func(") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "T%d", j) } if i > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "...V) R {\n") fmt.Fprintf(f, " return func(") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "t%d T%d", j, j) } if i > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "v ...V) R {\n") fmt.Fprintf(f, " return f(") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "t%d", j) } if i > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "v)\n") fmt.Fprintf(f, " }\n") fmt.Fprintf(f, "}\n") } func generateUnvariadic(f *os.File, i int) { // Create the nullary version fmt.Fprintf(f, "\n// Unvariadic%d converts a function taking %d parameters and a final variadic argument into a function with %d parameters but a final slice argument\n", i, i, i) fmt.Fprintf(f, "func Unvariadic%d[", i) for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "T%d", j) } if i > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "V, R any](f func(") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "T%d", j) } if i > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "...V) R) func(") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "T%d", j) } if i > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "[]V) R {\n") fmt.Fprintf(f, " return func(") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "t%d T%d", j, j) } if i > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "v []V) R {\n") fmt.Fprintf(f, " return f(") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "t%d", j) } if i > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "v...)\n") fmt.Fprintf(f, " }\n") fmt.Fprintf(f, "}\n") } func generateNullary(f *os.File, i int) { // Create the nullary version fmt.Fprintf(f, "\n// Nullary%d creates a parameter less function from a parameter less function and %d functions. When executed the first parameter less function gets executed and then the result is piped through the remaining functions\n", i, i-1) fmt.Fprintf(f, "func Nullary%d[F1 ~func() T1", i) for j := 2; j <= i; j++ { fmt.Fprintf(f, ", F%d ~func(T%d) T%d", j, j-1, j) } for j := 1; j <= i; j++ { fmt.Fprintf(f, ", T%d", j) } fmt.Fprintf(f, " any](f1 F1") for j := 2; j <= i; j++ { fmt.Fprintf(f, ", f%d F%d", j, j) } fmt.Fprintf(f, ") func() T%d {\n", i) fmt.Fprintf(f, " return func() T%d {\n", i) fmt.Fprintf(f, " return Pipe%d(f1()", i-1) for j := 2; j <= i; j++ { fmt.Fprintf(f, ", f%d", j) } fmt.Fprintf(f, ")\n") fmt.Fprintln(f, " }") fmt.Fprintln(f, "}") } func generateFlow(f *os.File, i int) { // Create the flow version fmt.Fprintf(f, "\n// Flow%d creates a function that takes an initial value t0 and successively applies %d functions where the input of a function is the return value of the previous function\n// The final return value is the result of the last function application\n", i, i) fmt.Fprintf(f, "func Flow%d[", i) for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "F%d ~func(T%d) T%d", j, j-1, j) } for j := 0; j <= i; j++ { fmt.Fprintf(f, ", T%d", j) } fmt.Fprintf(f, " any](") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "f%d F%d", j, j) } fmt.Fprintf(f, ") func(T0) T%d {\n", i) fmt.Fprintf(f, " return func(t0 T0) T%d {\n", i) fmt.Fprintf(f, " return Pipe%d(t0", i) for j := 1; j <= i; j++ { fmt.Fprintf(f, ", f%d", j) } fmt.Fprintf(f, ")\n") fmt.Fprintln(f, " }") fmt.Fprintln(f, "}") } func generatePipe(f *os.File, i int) { // Create the pipe version fmt.Fprintf(f, "\n// Pipe%d takes an initial value t0 and successively applies %d functions where the input of a function is the return value of the previous function\n// The final return value is the result of the last function application\n", i, i) fmt.Fprintf(f, "func Pipe%d[", i) for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "F%d ~func(T%d) T%d", j, j-1, j) } if i > 0 { fmt.Fprintf(f, ", ") } for j := 0; j <= i; j++ { if j > 0 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "T%d", j) } fmt.Fprintf(f, " any](t0 T0") for j := 1; j <= i; j++ { fmt.Fprintf(f, ", f%d F%d", j, j) } fmt.Fprintf(f, ") T%d {\n", i) for j := 1; j <= i; j++ { fmt.Fprintf(f, " t%d := f%d(t%d)\n", j, j, j-1) } fmt.Fprintf(f, " return t%d\n", i) fmt.Fprintln(f, "}") } func recurseCurry(f *os.File, indent string, total, count int) { if count == 1 { fmt.Fprintf(f, "%sreturn func(t%d T%d) T%d {\n", indent, total-1, total-1, total) fmt.Fprintf(f, "%s return f(t0", indent) for i := 1; i < total; i++ { fmt.Fprintf(f, ", t%d", i) } fmt.Fprintf(f, ")\n") fmt.Fprintf(f, "%s}\n", indent) } else { fmt.Fprintf(f, "%sreturn", indent) for i := total - count + 1; i <= total; i++ { fmt.Fprintf(f, " func(t%d T%d)", i-1, i-1) } fmt.Fprintf(f, " T%d {\n", total) recurseCurry(f, fmt.Sprintf(" %s", indent), total, count-1) fmt.Fprintf(f, "%s}\n", indent) } } func generateCurry(f *os.File, i int) { // Create the curry version fmt.Fprintf(f, "\n// Curry%d takes a function with %d parameters and returns a cascade of functions each taking only one parameter.\n// The inverse function is [Uncurry%d]\n", i, i, i) fmt.Fprintf(f, "func Curry%d[FCT ~func(T0", i) for j := 1; j < i; j++ { fmt.Fprintf(f, ", T%d", j) } fmt.Fprintf(f, ") T%d", i) // type arguments for j := 0; j <= i; j++ { fmt.Fprintf(f, ", T%d", j) } fmt.Fprintf(f, " any](f FCT) func(T0)") for j := 2; j <= i; j++ { fmt.Fprintf(f, " func(T%d)", j-1) } fmt.Fprintf(f, " T%d {\n", i) recurseCurry(f, " ", i, i) fmt.Fprintf(f, "}\n") } func generateUncurry(f *os.File, i int) { // Create the uncurry version fmt.Fprintf(f, "\n// Uncurry%d takes a cascade of %d functions each taking only one parameter and returns a function with %d parameters .\n// The inverse function is [Curry%d]\n", i, i, i, i) fmt.Fprintf(f, "func Uncurry%d[FCT ~func(T0)", i) for j := 1; j < i; j++ { fmt.Fprintf(f, " func(T%d)", j) } fmt.Fprintf(f, " T%d", i) // the type parameters for j := 0; j <= i; j++ { fmt.Fprintf(f, ", T%d", j) } fmt.Fprintf(f, " any](f FCT) func(") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "T%d", j-1) } fmt.Fprintf(f, ") T%d {\n", i) fmt.Fprintf(f, " return func(") for j := 1; j <= i; j++ { if j > 1 { fmt.Fprintf(f, ", ") } fmt.Fprintf(f, "t%d T%d", j-1, j-1) } fmt.Fprintf(f, ") T%d {\n", i) fmt.Fprintf(f, " return f") for j := 1; j <= i; j++ { fmt.Fprintf(f, "(t%d)", j-1) } fmt.Fprintln(f) fmt.Fprintf(f, " }\n") fmt.Fprintf(f, "}\n") } func generatePipeHelpers(filename string, count int) error { dir, err := os.Getwd() if err != nil { return err } absDir, err := filepath.Abs(dir) if err != nil { return err } pkg := filepath.Base(absDir) f, err := os.Create(filepath.Clean(filename)) if err != nil { return err } defer f.Close() // log log.Printf("Generating code in [%s] for package [%s] with [%d] repetitions ...", filename, pkg, count) // some header fmt.Fprintln(f, "// Code generated by go generate; DO NOT EDIT.") fmt.Fprintln(f, "// This file was generated by robots at") fmt.Fprintf(f, "// %s\n\n", time.Now()) fmt.Fprintf(f, "package %s\n", pkg) // pipe generatePipe(f, 0) // variadic generateVariadic(f, 0) // unvariadic generateUnvariadic(f, 0) // unsliced generateUnsliced(f, 0) for i := 1; i <= count; i++ { // pipe generatePipe(f, i) // flow generateFlow(f, i) // nullary generateNullary(f, i) // curry generateCurry(f, i) // uncurry generateUncurry(f, i) // variadic generateVariadic(f, i) // unvariadic generateUnvariadic(f, i) // unsliced generateUnsliced(f, i) } return nil } func PipeCommand() *C.Command { return &C.Command{ Name: "pipe", Usage: "generate code for pipe, flow, curry, etc", Description: "Code generation for pipe, flow, curry, etc", Flags: []C.Flag{ flagCount, flagFilename, }, Action: func(ctx *C.Context) error { return generatePipeHelpers( ctx.String(keyFilename), ctx.Int(keyCount), ) }, } }