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 }