github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/hcl2/model/visitor.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 model 16 17 import ( 18 "github.com/hashicorp/hcl/v2" 19 "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" 20 ) 21 22 // A BodyItemVisitor is a function that visits and optionally replaces the contents of a body item. 23 type BodyItemVisitor func(n BodyItem) (BodyItem, hcl.Diagnostics) 24 25 func BodyItemIdentityVisitor(n BodyItem) (BodyItem, hcl.Diagnostics) { 26 return n, nil 27 } 28 29 func visitBlock(n *Block, pre, post BodyItemVisitor) (BodyItem, hcl.Diagnostics) { 30 var diagnostics hcl.Diagnostics 31 32 var items []BodyItem 33 for _, item := range n.Body.Items { 34 newItem, diags := VisitBodyItem(item, pre, post) 35 diagnostics = append(diagnostics, diags...) 36 37 if newItem != nil { 38 items = append(items, newItem) 39 } 40 } 41 n.Body.Items = items 42 43 block, diags := post(n) 44 return block, append(diagnostics, diags...) 45 } 46 47 func VisitBodyItem(n BodyItem, pre, post BodyItemVisitor) (BodyItem, hcl.Diagnostics) { 48 if n == nil { 49 return nil, nil 50 } 51 52 if pre == nil { 53 pre = BodyItemIdentityVisitor 54 } 55 56 nn, preDiags := pre(n) 57 58 var postDiags hcl.Diagnostics 59 if post != nil { 60 switch n := nn.(type) { 61 case *Attribute: 62 nn, postDiags = post(n) 63 case *Block: 64 nn, postDiags = visitBlock(n, pre, post) 65 default: 66 contract.Failf("unexpected node type in visitExpression: %T", n) 67 return nil, nil 68 } 69 } 70 71 return nn, append(preDiags, postDiags...) 72 } 73 74 // An ExpressionVisitor is a function that visits and optionally replaces a node in an expression tree. 75 type ExpressionVisitor func(n Expression) (Expression, hcl.Diagnostics) 76 77 // IdentityVisitor is a ExpressionVisitor that returns the input node unchanged. 78 func IdentityVisitor(n Expression) (Expression, hcl.Diagnostics) { 79 return n, nil 80 } 81 82 func visitAnonymousFunction(n *AnonymousFunctionExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 83 var diagnostics hcl.Diagnostics 84 85 body, diags := VisitExpression(n.Body, pre, post) 86 diagnostics = append(diagnostics, diags...) 87 88 n.Body = body 89 90 expr, diags := post(n) 91 return expr, append(diagnostics, diags...) 92 } 93 94 func visitBinaryOp(n *BinaryOpExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 95 var diagnostics hcl.Diagnostics 96 97 left, diags := VisitExpression(n.LeftOperand, pre, post) 98 diagnostics = append(diagnostics, diags...) 99 100 right, diags := VisitExpression(n.RightOperand, pre, post) 101 diagnostics = append(diagnostics, diags...) 102 103 n.LeftOperand, n.RightOperand = left, right 104 105 expr, diags := post(n) 106 return expr, append(diagnostics, diags...) 107 } 108 109 func visitConditional(n *ConditionalExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 110 var diagnostics hcl.Diagnostics 111 112 condition, diags := VisitExpression(n.Condition, pre, post) 113 diagnostics = append(diagnostics, diags...) 114 115 trueResult, diags := VisitExpression(n.TrueResult, pre, post) 116 diagnostics = append(diagnostics, diags...) 117 118 falseResult, diags := VisitExpression(n.FalseResult, pre, post) 119 diagnostics = append(diagnostics, diags...) 120 121 n.Condition, n.TrueResult, n.FalseResult = condition, trueResult, falseResult 122 123 expr, diags := post(n) 124 return expr, append(diagnostics, diags...) 125 } 126 127 func visitFor(n *ForExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 128 var diagnostics hcl.Diagnostics 129 130 collection, diags := VisitExpression(n.Collection, pre, post) 131 diagnostics = append(diagnostics, diags...) 132 133 key, diags := VisitExpression(n.Key, pre, post) 134 diagnostics = append(diagnostics, diags...) 135 136 value, diags := VisitExpression(n.Value, pre, post) 137 diagnostics = append(diagnostics, diags...) 138 139 condition, diags := VisitExpression(n.Condition, pre, post) 140 diagnostics = append(diagnostics, diags...) 141 142 n.Collection, n.Key, n.Value, n.Condition = collection, key, value, condition 143 144 expr, diags := post(n) 145 return expr, append(diagnostics, diags...) 146 } 147 148 func visitFunctionCall(n *FunctionCallExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 149 var diagnostics hcl.Diagnostics 150 151 args, diags := visitExpressions(n.Args, pre, post) 152 diagnostics = append(diagnostics, diags...) 153 154 n.Args = args 155 156 expr, diags := post(n) 157 return expr, append(diagnostics, diags...) 158 } 159 160 func visitIndex(n *IndexExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 161 var diagnostics hcl.Diagnostics 162 163 collection, diags := VisitExpression(n.Collection, pre, post) 164 diagnostics = append(diagnostics, diags...) 165 166 key, diags := VisitExpression(n.Key, pre, post) 167 diagnostics = append(diagnostics, diags...) 168 169 n.Collection, n.Key = collection, key 170 171 expr, diags := post(n) 172 return expr, append(diagnostics, diags...) 173 } 174 175 func visitObjectCons(n *ObjectConsExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 176 var diagnostics hcl.Diagnostics 177 178 for i, item := range n.Items { 179 key, diags := VisitExpression(item.Key, pre, post) 180 diagnostics = append(diagnostics, diags...) 181 182 value, diags := VisitExpression(item.Value, pre, post) 183 diagnostics = append(diagnostics, diags...) 184 185 n.Items[i] = ObjectConsItem{Key: key, Value: value} 186 } 187 188 expr, diags := post(n) 189 return expr, append(diagnostics, diags...) 190 } 191 192 func visitRelativeTraversal(n *RelativeTraversalExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 193 var diagnostics hcl.Diagnostics 194 195 source, diags := VisitExpression(n.Source, pre, post) 196 diagnostics = append(diagnostics, diags...) 197 198 n.Source = source 199 200 expr, diags := post(n) 201 return expr, append(diagnostics, diags...) 202 } 203 204 func visitSplat(n *SplatExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 205 var diagnostics hcl.Diagnostics 206 207 source, diags := VisitExpression(n.Source, pre, post) 208 diagnostics = append(diagnostics, diags...) 209 210 each, diags := VisitExpression(n.Each, pre, post) 211 diagnostics = append(diagnostics, diags...) 212 213 n.Source, n.Each = source, each 214 215 expr, diags := post(n) 216 return expr, append(diagnostics, diags...) 217 } 218 219 func visitTemplate(n *TemplateExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 220 var diagnostics hcl.Diagnostics 221 222 parts, diags := visitExpressions(n.Parts, pre, post) 223 diagnostics = append(diagnostics, diags...) 224 225 n.Parts = parts 226 227 expr, diags := post(n) 228 return expr, append(diagnostics, diags...) 229 } 230 231 func visitTemplateJoin(n *TemplateJoinExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 232 var diagnostics hcl.Diagnostics 233 234 tuple, diags := VisitExpression(n.Tuple, pre, post) 235 diagnostics = append(diagnostics, diags...) 236 237 n.Tuple = tuple 238 239 expr, diags := post(n) 240 return expr, append(diagnostics, diags...) 241 } 242 243 func visitTupleCons(n *TupleConsExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 244 var diagnostics hcl.Diagnostics 245 246 expressions, diags := visitExpressions(n.Expressions, pre, post) 247 diagnostics = append(diagnostics, diags...) 248 249 n.Expressions = expressions 250 251 expr, diags := post(n) 252 return expr, append(diagnostics, diags...) 253 } 254 255 func visitUnaryOp(n *UnaryOpExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 256 var diagnostics hcl.Diagnostics 257 258 operand, diags := VisitExpression(n.Operand, pre, post) 259 diagnostics = append(diagnostics, diags...) 260 261 n.Operand = operand 262 263 expr, diags := post(n) 264 return expr, append(diagnostics, diags...) 265 } 266 267 func visitExpressions(ns []Expression, pre, post ExpressionVisitor) ([]Expression, hcl.Diagnostics) { 268 var diagnostics hcl.Diagnostics 269 270 nils := 0 271 for i, e := range ns { 272 ee, diags := VisitExpression(e, pre, post) 273 diagnostics = append(diagnostics, diags...) 274 if ee == nil { 275 nils++ 276 } 277 ns[i] = ee 278 } 279 if nils == 0 { 280 return ns, diagnostics 281 } else if nils == len(ns) { 282 return []Expression{}, diagnostics 283 } 284 285 nns := make([]Expression, 0, len(ns)-nils) 286 for _, e := range ns { 287 if e != nil { 288 nns = append(nns, e) 289 } 290 } 291 return nns, diagnostics 292 } 293 294 // VisitExpression visits each node in an expression tree using the given pre- and post-order visitors. If the preorder 295 // visitor returns a new node, that node's descendents will be visited. VisitExpression returns the result of the 296 // post-order visitor. All diagnostics are accumulated. 297 func VisitExpression(n Expression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) { 298 if n == nil { 299 return nil, nil 300 } 301 302 if pre == nil { 303 pre = IdentityVisitor 304 } 305 306 nn, preDiags := pre(n) 307 308 var postDiags hcl.Diagnostics 309 if post != nil { 310 switch n := nn.(type) { 311 case *AnonymousFunctionExpression: 312 nn, postDiags = visitAnonymousFunction(n, pre, post) 313 case *BinaryOpExpression: 314 nn, postDiags = visitBinaryOp(n, pre, post) 315 case *ConditionalExpression: 316 nn, postDiags = visitConditional(n, pre, post) 317 case *ErrorExpression: 318 nn, postDiags = post(n) 319 case *ForExpression: 320 nn, postDiags = visitFor(n, pre, post) 321 case *FunctionCallExpression: 322 nn, postDiags = visitFunctionCall(n, pre, post) 323 case *IndexExpression: 324 nn, postDiags = visitIndex(n, pre, post) 325 case *LiteralValueExpression: 326 nn, postDiags = post(n) 327 case *ObjectConsExpression: 328 nn, postDiags = visitObjectCons(n, pre, post) 329 case *RelativeTraversalExpression: 330 nn, postDiags = visitRelativeTraversal(n, pre, post) 331 case *ScopeTraversalExpression: 332 nn, postDiags = post(n) 333 case *SplatExpression: 334 nn, postDiags = visitSplat(n, pre, post) 335 case *TemplateExpression: 336 nn, postDiags = visitTemplate(n, pre, post) 337 case *TemplateJoinExpression: 338 nn, postDiags = visitTemplateJoin(n, pre, post) 339 case *TupleConsExpression: 340 nn, postDiags = visitTupleCons(n, pre, post) 341 case *UnaryOpExpression: 342 nn, postDiags = visitUnaryOp(n, pre, post) 343 default: 344 contract.Failf("unexpected node type in visitExpression: %T", n) 345 return nil, nil 346 } 347 } 348 349 return nn, append(preDiags, postDiags...) 350 } 351 352 func visitBlockExpressions(n *Block, pre, post ExpressionVisitor) hcl.Diagnostics { 353 var diagnostics hcl.Diagnostics 354 355 for _, item := range n.Body.Items { 356 diags := VisitExpressions(item, pre, post) 357 diagnostics = append(diagnostics, diags...) 358 } 359 360 return diagnostics 361 } 362 363 // VisitExpressions visits each expression that descends from the given body item. 364 func VisitExpressions(n BodyItem, pre, post ExpressionVisitor) hcl.Diagnostics { 365 if n == nil { 366 return nil 367 } 368 369 if pre == nil { 370 pre = IdentityVisitor 371 } 372 373 switch n := n.(type) { 374 case *Attribute: 375 v, diags := VisitExpression(n.Value, pre, post) 376 n.Value = v 377 return diags 378 case *Block: 379 return visitBlockExpressions(n, pre, post) 380 default: 381 contract.Failf("unexpected node type in visitExpression: %T", n) 382 return nil 383 } 384 }