github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/hcl2/model/format/gen.go (about)

     1  // Copyright 2016-2020, Pulumi Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package format
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"math"
    21  
    22  	"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model"
    23  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
    24  )
    25  
    26  // ExpressionGenerator is an interface that can be implemented in order to generate code for semantically-analyzed HCL2
    27  // expressions using a Formatter.
    28  type ExpressionGenerator interface {
    29  	// GetPrecedence returns the precedence for the indicated expression. Lower numbers bind more tightly than higher
    30  	// numbers.
    31  	GetPrecedence(expr model.Expression) int
    32  
    33  	// GenAnonymousFunctionExpression generates code for an AnonymousFunctionExpression.
    34  	GenAnonymousFunctionExpression(w io.Writer, expr *model.AnonymousFunctionExpression)
    35  	// GenBinaryOpExpression generates code for a BinaryOpExpression.
    36  	GenBinaryOpExpression(w io.Writer, expr *model.BinaryOpExpression)
    37  	// GenConditionalExpression generates code for a ConditionalExpression.
    38  	GenConditionalExpression(w io.Writer, expr *model.ConditionalExpression)
    39  	// GenForExpression generates code for a ForExpression.
    40  	GenForExpression(w io.Writer, expr *model.ForExpression)
    41  	// GenFunctionCallExpression generates code for a FunctionCallExpression.
    42  	GenFunctionCallExpression(w io.Writer, expr *model.FunctionCallExpression)
    43  	// GenIndexExpression generates code for an IndexExpression.
    44  	GenIndexExpression(w io.Writer, expr *model.IndexExpression)
    45  	// GenLiteralValueExpression generates code for a LiteralValueExpression.
    46  	GenLiteralValueExpression(w io.Writer, expr *model.LiteralValueExpression)
    47  	// GenObjectConsExpression generates code for an ObjectConsExpression.
    48  	GenObjectConsExpression(w io.Writer, expr *model.ObjectConsExpression)
    49  	// GenRelativeTraversalExpression generates code for a RelativeTraversalExpression.
    50  	GenRelativeTraversalExpression(w io.Writer, expr *model.RelativeTraversalExpression)
    51  	// GenScopeTraversalExpression generates code for a ScopeTraversalExpression.
    52  	GenScopeTraversalExpression(w io.Writer, expr *model.ScopeTraversalExpression)
    53  	// GenSplatExpression generates code for a SplatExpression.
    54  	GenSplatExpression(w io.Writer, expr *model.SplatExpression)
    55  	// GenTemplateExpression generates code for a TemplateExpression.
    56  	GenTemplateExpression(w io.Writer, expr *model.TemplateExpression)
    57  	// GenTemplateJoinExpression generates code for a TemplateJoinExpression.
    58  	GenTemplateJoinExpression(w io.Writer, expr *model.TemplateJoinExpression)
    59  	// GenTupleConsExpression generates code for a TupleConsExpression.
    60  	GenTupleConsExpression(w io.Writer, expr *model.TupleConsExpression)
    61  	// GenUnaryOpExpression generates code for a UnaryOpExpression.
    62  	GenUnaryOpExpression(w io.Writer, expr *model.UnaryOpExpression)
    63  }
    64  
    65  // Formatter is a convenience type that implements a number of common utilities used to emit source code. It implements
    66  // the io.Writer interface.
    67  type Formatter struct {
    68  	// The current indent level as a string.
    69  	Indent string
    70  
    71  	// The ExpressionGenerator to use in {G,Fg}en{,f}
    72  	g ExpressionGenerator
    73  }
    74  
    75  // NewFormatter creates a new emitter targeting the given io.Writer that will use the given ExpressionGenerator when
    76  // generating code.
    77  func NewFormatter(g ExpressionGenerator) *Formatter {
    78  	return &Formatter{g: g}
    79  }
    80  
    81  // Indented bumps the current indentation level, invokes the given function, and then resets the indentation level to
    82  // its prior value.
    83  func (e *Formatter) Indented(f func()) {
    84  	e.Indent += "    "
    85  	f()
    86  	e.Indent = e.Indent[:len(e.Indent)-4]
    87  }
    88  
    89  // Fprint prints one or more values to the generator's output stream.
    90  func (e *Formatter) Fprint(w io.Writer, a ...interface{}) {
    91  	_, err := fmt.Fprint(w, a...)
    92  	contract.IgnoreError(err)
    93  }
    94  
    95  // Fprintln prints one or more values to the generator's output stream, followed by a newline.
    96  func (e *Formatter) Fprintln(w io.Writer, a ...interface{}) {
    97  	e.Fprint(w, a...)
    98  	e.Fprint(w, "\n")
    99  }
   100  
   101  // Fprintf prints a formatted message to the generator's output stream.
   102  func (e *Formatter) Fprintf(w io.Writer, format string, a ...interface{}) {
   103  	_, err := fmt.Fprintf(w, format, a...)
   104  	contract.IgnoreError(err)
   105  }
   106  
   107  func (e *Formatter) gen(w io.Writer, parentPrecedence int, rhs bool, x model.Expression) {
   108  	precedence := e.g.GetPrecedence(x)
   109  	switch {
   110  	case precedence > parentPrecedence:
   111  		// OK
   112  	case precedence < parentPrecedence || rhs:
   113  		_, err := fmt.Fprint(w, "(")
   114  		contract.AssertNoError(err)
   115  
   116  		defer func() {
   117  			_, err = fmt.Fprint(w, ")")
   118  			contract.AssertNoError(err)
   119  		}()
   120  	}
   121  
   122  	switch x := x.(type) {
   123  	case *model.AnonymousFunctionExpression:
   124  		e.g.GenAnonymousFunctionExpression(w, x)
   125  	case *model.BinaryOpExpression:
   126  		e.g.GenBinaryOpExpression(w, x)
   127  	case *model.ConditionalExpression:
   128  		e.g.GenConditionalExpression(w, x)
   129  	case *model.ForExpression:
   130  		e.g.GenForExpression(w, x)
   131  	case *model.FunctionCallExpression:
   132  		e.g.GenFunctionCallExpression(w, x)
   133  	case *model.IndexExpression:
   134  		e.g.GenIndexExpression(w, x)
   135  	case *model.LiteralValueExpression:
   136  		e.g.GenLiteralValueExpression(w, x)
   137  	case *model.ObjectConsExpression:
   138  		e.g.GenObjectConsExpression(w, x)
   139  	case *model.RelativeTraversalExpression:
   140  		e.g.GenRelativeTraversalExpression(w, x)
   141  	case *model.ScopeTraversalExpression:
   142  		e.g.GenScopeTraversalExpression(w, x)
   143  	case *model.SplatExpression:
   144  		e.g.GenSplatExpression(w, x)
   145  	case *model.TemplateExpression:
   146  		e.g.GenTemplateExpression(w, x)
   147  	case *model.TemplateJoinExpression:
   148  		e.g.GenTemplateJoinExpression(w, x)
   149  	case *model.TupleConsExpression:
   150  		e.g.GenTupleConsExpression(w, x)
   151  	case *model.UnaryOpExpression:
   152  		e.g.GenUnaryOpExpression(w, x)
   153  	default:
   154  		contract.Failf("unexpected expression node of type %T (%v)", x, x.SyntaxNode().Range())
   155  	}
   156  }
   157  
   158  // Fgen generates code for a list of strings and expression trees. The former are written directly to the destination;
   159  // the latter are recursively generated using the appropriate gen* functions.
   160  func (e *Formatter) Fgen(w io.Writer, vs ...interface{}) {
   161  	for _, v := range vs {
   162  		if x, ok := v.(model.Expression); ok {
   163  			e.gen(w, math.MaxInt32, false, x)
   164  		} else {
   165  			_, err := fmt.Fprint(w, v)
   166  			contract.AssertNoError(err)
   167  		}
   168  	}
   169  }
   170  
   171  // Fgenf generates code using a format string and its arguments. Any arguments that are BoundNode values are wrapped in
   172  // a Func that calls the appropriate recursive generation function. This allows for the composition of standard
   173  // format strings with expression/property code gen (e.e. `e.genf(w, ".apply(__arg0 => %v)", then)`, where `then` is
   174  // an expression tree).
   175  func (e *Formatter) Fgenf(w io.Writer, format string, args ...interface{}) {
   176  	for i := range args {
   177  		if node, ok := args[i].(model.Expression); ok {
   178  			args[i] = Func(func(f fmt.State, c rune) {
   179  				parentPrecedence := 0
   180  				if pp, ok := f.Precision(); ok {
   181  					parentPrecedence = pp
   182  				}
   183  				rhs := c == 'o'
   184  				e.gen(f, parentPrecedence, rhs, node)
   185  			})
   186  		}
   187  	}
   188  	fmt.Fprintf(w, format, args...)
   189  }