2023-07-23 22:05:54 +02:00
// 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.
2023-07-10 12:40:27 +02:00
package cli
import (
"fmt"
"log"
"os"
"path/filepath"
"time"
C "github.com/urfave/cli/v2"
)
2023-09-12 10:52:39 +02:00
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 , "}" )
}
2023-07-13 16:14:38 +02:00
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" )
2023-07-10 12:40:27 +02:00
2023-09-12 10:52:39 +02:00
fmt . Fprintf ( f , "}\n" )
2023-07-13 16:14:38 +02:00
}
func generateUnvariadic ( f * os . File , i int ) {
2023-07-10 12:40:27 +02:00
// Create the nullary version
2023-07-13 16:14:38 +02:00
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(" )
2023-07-10 12:40:27 +02:00
for j := 1 ; j <= i ; j ++ {
if j > 1 {
fmt . Fprintf ( f , ", " )
}
fmt . Fprintf ( f , "T%d" , j )
}
2023-07-13 16:14:38 +02:00
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" )
2023-09-12 10:52:39 +02:00
fmt . Fprintf ( f , "}\n" )
2023-07-13 16:14:38 +02:00
}
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" )
2023-07-10 12:40:27 +02:00
for j := 2 ; j <= i ; j ++ {
2023-07-13 16:14:38 +02:00
fmt . Fprintf ( f , ", f%d F%d" , j , j )
2023-07-10 12:40:27 +02:00
}
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
2023-07-13 16:14:38 +02:00
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 )
2023-07-10 12:40:27 +02:00
for j := 1 ; j <= i ; j ++ {
2023-07-13 16:14:38 +02:00
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 ++ {
2023-07-10 12:40:27 +02:00
fmt . Fprintf ( f , ", T%d" , j )
}
fmt . Fprintf ( f , " any](" )
for j := 1 ; j <= i ; j ++ {
if j > 1 {
fmt . Fprintf ( f , ", " )
}
2023-07-13 16:14:38 +02:00
fmt . Fprintf ( f , "f%d F%d" , j , j )
2023-07-10 12:40:27 +02:00
}
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
2023-07-13 16:14:38 +02:00
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 )
2023-07-10 12:40:27 +02:00
for j := 1 ; j <= i ; j ++ {
2023-07-13 16:14:38 +02:00
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 , ", " )
2023-07-10 12:40:27 +02:00
}
2023-07-13 16:14:38 +02:00
for j := 0 ; j <= i ; j ++ {
if j > 0 {
fmt . Fprintf ( f , ", " )
}
fmt . Fprintf ( f , "T%d" , j )
}
2023-07-10 12:40:27 +02:00
fmt . Fprintf ( f , " any](t0 T0" )
for j := 1 ; j <= i ; j ++ {
2023-07-13 16:14:38 +02:00
fmt . Fprintf ( f , ", f%d F%d" , j , j )
2023-07-10 12:40:27 +02:00
}
fmt . Fprintf ( f , ") T%d {\n" , i )
2024-01-31 21:50:33 +01:00
fmt . Fprintf ( f , " return " )
for j := i ; j >= 1 ; j -- {
fmt . Fprintf ( f , "f%d(" , j )
}
fmt . Fprintf ( f , "t0" )
2023-07-10 12:40:27 +02:00
for j := 1 ; j <= i ; j ++ {
2024-01-31 21:50:33 +01:00
fmt . Fprintf ( f , ")" )
2023-07-10 12:40:27 +02:00
}
2024-01-31 21:50:33 +01:00
fmt . Fprintf ( f , "\n" )
2023-07-10 12:40:27 +02:00
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 )
2023-12-18 09:46:46 +01:00
fmt . Fprintf ( f , "func Curry%d[FCT ~func(T0" , i )
for j := 1 ; j < i ; j ++ {
2023-07-10 12:40:27 +02:00
fmt . Fprintf ( f , ", T%d" , j )
}
2023-12-18 09:46:46 +01:00
fmt . Fprintf ( f , ") T%d" , i )
// type arguments
for j := 0 ; j <= i ; j ++ {
fmt . Fprintf ( f , ", T%d" , j )
2023-07-10 12:40:27 +02:00
}
2023-12-18 09:46:46 +01:00
fmt . Fprintf ( f , " any](f FCT) func(T0)" )
2023-07-10 12:40:27 +02:00
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 )
2023-12-18 09:46:46 +01:00
fmt . Fprintf ( f , "func Uncurry%d[FCT ~func(T0)" , i )
for j := 1 ; j < i ; j ++ {
fmt . Fprintf ( f , " func(T%d)" , j )
2023-07-10 12:40:27 +02:00
}
2023-12-18 09:46:46 +01:00
fmt . Fprintf ( f , " T%d" , i )
// the type parameters
for j := 0 ; j <= i ; j ++ {
fmt . Fprintf ( f , ", T%d" , j )
2023-07-10 12:40:27 +02:00
}
2023-12-18 09:46:46 +01:00
fmt . Fprintf ( f , " any](f FCT) func(" )
2023-07-10 12:40:27 +02:00
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" )
}
2023-07-13 16:14:38 +02:00
func generatePipeHelpers ( filename string , count int ) error {
2023-07-10 12:40:27 +02:00
dir , err := os . Getwd ( )
if err != nil {
return err
}
absDir , err := filepath . Abs ( dir )
if err != nil {
return err
}
pkg := filepath . Base ( absDir )
2023-07-13 22:24:04 +02:00
f , err := os . Create ( filepath . Clean ( filename ) )
2023-07-10 12:40:27 +02:00
if err != nil {
return err
}
defer f . Close ( )
// log
2023-07-13 16:14:38 +02:00
log . Printf ( "Generating code in [%s] for package [%s] with [%d] repetitions ..." , filename , pkg , count )
2023-07-10 12:40:27 +02:00
// some header
fmt . Fprintln ( f , "// Code generated by go generate; DO NOT EDIT." )
fmt . Fprintln ( f , "// This file was generated by robots at" )
2023-08-11 11:56:11 +02:00
fmt . Fprintf ( f , "// %s\n\n" , time . Now ( ) )
2023-07-10 12:40:27 +02:00
fmt . Fprintf ( f , "package %s\n" , pkg )
2023-07-13 16:14:38 +02:00
// pipe
generatePipe ( f , 0 )
// variadic
generateVariadic ( f , 0 )
// unvariadic
generateUnvariadic ( f , 0 )
2023-09-12 10:52:39 +02:00
// unsliced
generateUnsliced ( f , 0 )
2023-07-13 16:14:38 +02:00
2023-07-10 12:40:27 +02:00
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 )
2023-07-13 16:14:38 +02:00
// variadic
generateVariadic ( f , i )
// unvariadic
generateUnvariadic ( f , i )
2023-09-12 10:52:39 +02:00
// unsliced
generateUnsliced ( f , i )
2023-07-10 12:40:27 +02:00
}
return nil
}
func PipeCommand ( ) * C . Command {
return & C . Command {
2023-07-13 16:14:38 +02:00
Name : "pipe" ,
Usage : "generate code for pipe, flow, curry, etc" ,
Description : "Code generation for pipe, flow, curry, etc" ,
2023-07-10 12:40:27 +02:00
Flags : [ ] C . Flag {
2023-07-13 16:14:38 +02:00
flagCount ,
flagFilename ,
2023-07-10 12:40:27 +02:00
} ,
Action : func ( ctx * C . Context ) error {
2023-07-13 16:14:38 +02:00
return generatePipeHelpers (
ctx . String ( keyFilename ) ,
ctx . Int ( keyCount ) ,
)
2023-07-10 12:40:27 +02:00
} ,
}
}