github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/nodejs/gen_program_expressions.go (about) 1 package nodejs 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "math/big" 8 "strings" 9 10 "github.com/hashicorp/hcl/v2" 11 "github.com/hashicorp/hcl/v2/hclsyntax" 12 "github.com/zclconf/go-cty/cty" 13 "github.com/zclconf/go-cty/cty/convert" 14 15 "github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model" 16 "github.com/pulumi/pulumi/pkg/v3/codegen/pcl" 17 "github.com/pulumi/pulumi/pkg/v3/codegen/schema" 18 "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" 19 ) 20 21 type nameInfo int 22 23 func (nameInfo) Format(name string) string { 24 return makeValidIdentifier(name) 25 } 26 27 func (g *generator) lowerExpression(expr model.Expression, typ model.Type) model.Expression { 28 // TODO(pdg): diagnostics 29 if g.asyncMain { 30 expr = g.awaitInvokes(expr) 31 } 32 expr = pcl.RewritePropertyReferences(expr) 33 expr, diags := pcl.RewriteApplies(expr, nameInfo(0), !g.asyncMain) 34 if typ != nil { 35 var convertDiags hcl.Diagnostics 36 expr, convertDiags = pcl.RewriteConversions(expr, typ) 37 diags = diags.Extend(convertDiags) 38 } 39 expr, lowerProxyDiags := g.lowerProxyApplies(expr) 40 diags = diags.Extend(lowerProxyDiags) 41 g.diagnostics = g.diagnostics.Extend(diags) 42 return expr 43 } 44 45 func (g *generator) GetPrecedence(expr model.Expression) int { 46 // Precedence is derived from 47 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence. 48 switch expr := expr.(type) { 49 case *model.ConditionalExpression: 50 return 4 51 case *model.BinaryOpExpression: 52 switch expr.Operation { 53 case hclsyntax.OpLogicalOr: 54 return 5 55 case hclsyntax.OpLogicalAnd: 56 return 6 57 case hclsyntax.OpEqual, hclsyntax.OpNotEqual: 58 return 11 59 case hclsyntax.OpGreaterThan, hclsyntax.OpGreaterThanOrEqual, hclsyntax.OpLessThan, 60 hclsyntax.OpLessThanOrEqual: 61 return 12 62 case hclsyntax.OpAdd, hclsyntax.OpSubtract: 63 return 14 64 case hclsyntax.OpMultiply, hclsyntax.OpDivide, hclsyntax.OpModulo: 65 return 15 66 default: 67 contract.Failf("unexpected binary expression %v", expr) 68 } 69 case *model.UnaryOpExpression: 70 return 17 71 case *model.FunctionCallExpression: 72 switch expr.Name { 73 case intrinsicAwait: 74 return 17 75 case intrinsicInterpolate: 76 return 22 77 default: 78 return 20 79 } 80 case *model.ForExpression, *model.IndexExpression, *model.RelativeTraversalExpression, *model.SplatExpression, 81 *model.TemplateJoinExpression: 82 return 20 83 case *model.AnonymousFunctionExpression, *model.LiteralValueExpression, *model.ObjectConsExpression, 84 *model.ScopeTraversalExpression, *model.TemplateExpression, *model.TupleConsExpression: 85 return 22 86 default: 87 contract.Failf("unexpected expression %v of type %T", expr, expr) 88 } 89 return 0 90 } 91 92 func (g *generator) GenAnonymousFunctionExpression(w io.Writer, expr *model.AnonymousFunctionExpression) { 93 switch len(expr.Signature.Parameters) { 94 case 0: 95 g.Fgen(w, "()") 96 case 1: 97 g.Fgenf(w, "%s", expr.Signature.Parameters[0].Name) 98 default: 99 g.Fgen(w, "([") 100 for i, p := range expr.Signature.Parameters { 101 if i > 0 { 102 g.Fgen(w, ", ") 103 } 104 g.Fgenf(w, "%s", p.Name) 105 } 106 g.Fgen(w, "])") 107 } 108 109 g.Fgenf(w, " => %.v", expr.Body) 110 } 111 112 func (g *generator) GenBinaryOpExpression(w io.Writer, expr *model.BinaryOpExpression) { 113 opstr, precedence := "", g.GetPrecedence(expr) 114 switch expr.Operation { 115 case hclsyntax.OpAdd: 116 opstr = "+" 117 case hclsyntax.OpDivide: 118 opstr = "/" 119 case hclsyntax.OpEqual: 120 opstr = "==" 121 case hclsyntax.OpGreaterThan: 122 opstr = ">" 123 case hclsyntax.OpGreaterThanOrEqual: 124 opstr = ">=" 125 case hclsyntax.OpLessThan: 126 opstr = "<" 127 case hclsyntax.OpLessThanOrEqual: 128 opstr = "<=" 129 case hclsyntax.OpLogicalAnd: 130 opstr = "&&" 131 case hclsyntax.OpLogicalOr: 132 opstr = "||" 133 case hclsyntax.OpModulo: 134 opstr = "%" 135 case hclsyntax.OpMultiply: 136 opstr = "*" 137 case hclsyntax.OpNotEqual: 138 opstr = "!=" 139 case hclsyntax.OpSubtract: 140 opstr = "-" 141 default: 142 opstr, precedence = ",", 1 143 } 144 145 g.Fgenf(w, "%.[1]*[2]v %[3]v %.[1]*[4]o", precedence, expr.LeftOperand, opstr, expr.RightOperand) 146 } 147 148 func (g *generator) GenConditionalExpression(w io.Writer, expr *model.ConditionalExpression) { 149 g.Fgenf(w, "%.4v ? %.4v : %.4v", expr.Condition, expr.TrueResult, expr.FalseResult) 150 } 151 152 func (g *generator) GenForExpression(w io.Writer, expr *model.ForExpression) { 153 switch expr.Collection.Type().(type) { 154 case *model.ListType, *model.TupleType: 155 if expr.KeyVariable == nil { 156 g.Fgenf(w, "%.20v", expr.Collection) 157 } else { 158 g.Fgenf(w, "%.20v.map((v, k) => [k, v])", expr.Collection) 159 } 160 case *model.MapType, *model.ObjectType: 161 if expr.KeyVariable == nil { 162 g.Fgenf(w, "Object.values(%.v)", expr.Collection) 163 } else { 164 g.Fgenf(w, "Object.entries(%.v)", expr.Collection) 165 } 166 } 167 168 fnParams, reduceParams := expr.ValueVariable.Name, expr.ValueVariable.Name 169 if expr.KeyVariable != nil { 170 reduceParams = fmt.Sprintf("[%.v, %.v]", expr.KeyVariable.Name, expr.ValueVariable.Name) 171 fnParams = fmt.Sprintf("(%v)", reduceParams) 172 } 173 174 if expr.Condition != nil { 175 g.Fgenf(w, ".filter(%s => %.v)", fnParams, expr.Condition) 176 } 177 178 if expr.Key != nil { 179 // TODO(pdg): grouping 180 g.Fgenf(w, ".reduce((__obj, %s) => { ...__obj, [%.v]: %.v })", reduceParams, expr.Key, expr.Value) 181 } else { 182 g.Fgenf(w, ".map(%s => %.v)", fnParams, expr.Value) 183 } 184 } 185 186 func (g *generator) genApply(w io.Writer, expr *model.FunctionCallExpression) { 187 // Extract the list of outputs and the continuation expression from the `__apply` arguments. 188 applyArgs, then := pcl.ParseApplyCall(expr) 189 190 // If all of the arguments are promises, use promise methods. If any argument is an output, convert all other args 191 // to outputs and use output methods. 192 anyOutputs := false 193 for _, arg := range applyArgs { 194 if isOutputType(arg.Type()) { 195 anyOutputs = true 196 } 197 } 198 199 apply, all := "then", "Promise.all" 200 if anyOutputs { 201 apply, all = "apply", "pulumi.all" 202 } 203 204 if len(applyArgs) == 1 { 205 // If we only have a single output, just generate a normal `.apply` or `.then`. 206 g.Fgenf(w, "%.20v.%v(%.v)", applyArgs[0], apply, then) 207 } else { 208 // Otherwise, generate a call to `pulumi.all([]).apply()`. 209 g.Fgenf(w, "%v([", all) 210 for i, o := range applyArgs { 211 if i > 0 { 212 g.Fgen(w, ", ") 213 } 214 g.Fgenf(w, "%v", o) 215 } 216 g.Fgenf(w, "]).%v(%.v)", apply, then) 217 } 218 } 219 220 // functionName computes the NodeJS package, module, and name for the given function token. 221 func functionName(tokenArg model.Expression) (string, string, string, hcl.Diagnostics) { 222 token := tokenArg.(*model.TemplateExpression).Parts[0].(*model.LiteralValueExpression).Value.AsString() 223 tokenRange := tokenArg.SyntaxNode().Range() 224 225 // Compute the resource type from the Pulumi type token. 226 pkg, module, member, diagnostics := pcl.DecomposeToken(token, tokenRange) 227 return pkg, strings.Replace(module, "/", ".", -1), member, diagnostics 228 } 229 230 func (g *generator) genRange(w io.Writer, call *model.FunctionCallExpression, entries bool) { 231 var from, to model.Expression 232 switch len(call.Args) { 233 case 1: 234 from, to = &model.LiteralValueExpression{Value: cty.NumberIntVal(0)}, call.Args[0] 235 case 2: 236 from, to = call.Args[0], call.Args[1] 237 default: 238 contract.Failf("expected range() to have exactly 1 or 2 args; got %v", len(call.Args)) 239 } 240 241 genPrefix := func() { g.Fprint(w, "((from, to) => (new Array(to - from))") } 242 mapValue := "from + i" 243 genSuffix := func() { g.Fgenf(w, ")(%.v, %.v)", from, to) } 244 245 if litFrom, ok := from.(*model.LiteralValueExpression); ok { 246 fromV, err := convert.Convert(litFrom.Value, cty.Number) 247 contract.Assert(err == nil) 248 249 from, _ := fromV.AsBigFloat().Int64() 250 if litTo, ok := to.(*model.LiteralValueExpression); ok { 251 toV, err := convert.Convert(litTo.Value, cty.Number) 252 contract.Assert(err == nil) 253 254 to, _ := toV.AsBigFloat().Int64() 255 if from == 0 { 256 mapValue = "i" 257 } else { 258 mapValue = fmt.Sprintf("%d + i", from) 259 } 260 genPrefix = func() { g.Fprintf(w, "(new Array(%d))", to-from) } 261 genSuffix = func() {} 262 } else if from == 0 { 263 genPrefix = func() { g.Fgenf(w, "(new Array(%.v))", to) } 264 mapValue = "i" 265 genSuffix = func() {} 266 } 267 } 268 269 if entries { 270 mapValue = fmt.Sprintf("{key: %[1]s, value: %[1]s}", mapValue) 271 } 272 273 genPrefix() 274 g.Fprintf(w, ".map((_, i) => %v)", mapValue) 275 genSuffix() 276 } 277 278 var functionImports = map[string][]string{ 279 intrinsicInterpolate: {"@pulumi/pulumi"}, 280 "fileArchive": {"@pulumi/pulumi"}, 281 "remoteArchive": {"@pulumi/pulumi"}, 282 "assetArchive": {"@pulumi/pulumi"}, 283 "fileAsset": {"@pulumi/pulumi"}, 284 "stringAsset": {"@pulumi/pulumi"}, 285 "remoteAsset": {"@pulumi/pulumi"}, 286 "filebase64": {"fs"}, 287 "filebase64sha256": {"fs", "crypto"}, 288 "readFile": {"fs"}, 289 "readDir": {"fs"}, 290 "sha1": {"crypto"}, 291 } 292 293 func (g *generator) getFunctionImports(x *model.FunctionCallExpression) []string { 294 if x.Name != pcl.Invoke { 295 return functionImports[x.Name] 296 } 297 298 pkg, _, _, diags := functionName(x.Args[0]) 299 contract.Assert(len(diags) == 0) 300 return []string{"@pulumi/" + pkg} 301 } 302 303 func enumName(enum *model.EnumType) (string, error) { 304 components := strings.Split(enum.Token, ":") 305 contract.Assertf(len(components) == 3, "malformed token %v", enum.Token) 306 name := tokenToName(enum.Token) 307 pkg := makeValidIdentifier(components[0]) 308 e, ok := pcl.GetSchemaForType(enum) 309 if !ok { 310 return "", fmt.Errorf("Could not get associated enum") 311 } 312 if name := e.(*schema.EnumType).Package.Language["nodejs"].(NodePackageInfo).PackageName; name != "" { 313 pkg = name 314 } 315 if mod := components[1]; mod != "" && mod != "index" { 316 if pkg := e.(*schema.EnumType).Package; pkg != nil { 317 mod = moduleName(mod, pkg) 318 } 319 pkg += "." + mod 320 } 321 return fmt.Sprintf("%s.%s", pkg, name), nil 322 } 323 324 func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionCallExpression) { 325 switch expr.Name { 326 case pcl.IntrinsicConvert: 327 from := expr.Args[0] 328 to := pcl.LowerConversion(from, expr.Signature.ReturnType) 329 output, isOutput := to.(*model.OutputType) 330 if isOutput { 331 to = output.ElementType 332 } 333 switch to := to.(type) { 334 case *model.EnumType: 335 if enum, err := enumName(to); err == nil { 336 if isOutput { 337 g.Fgenf(w, "%.v.apply((x) => %s[x])", from, enum) 338 } else { 339 pcl.GenEnum(to, from, func(member *schema.Enum) { 340 memberTag, err := enumMemberName(tokenToName(to.Token), member) 341 contract.AssertNoErrorf(err, "Failed to get member name on enum '%s'", enum) 342 g.Fgenf(w, "%s.%s", enum, memberTag) 343 }, func(from model.Expression) { 344 g.Fgenf(w, "%s[%.v]", enum, from) 345 }) 346 } 347 } else { 348 g.Fgenf(w, "%v", from) 349 } 350 default: 351 g.Fgenf(w, "%v", from) 352 } 353 case pcl.IntrinsicApply: 354 g.genApply(w, expr) 355 case intrinsicAwait: 356 g.Fgenf(w, "await %.17v", expr.Args[0]) 357 case intrinsicInterpolate: 358 g.Fgen(w, "pulumi.interpolate`") 359 for _, part := range expr.Args { 360 if lit, ok := part.(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) { 361 g.Fgen(w, lit.Value.AsString()) 362 } else { 363 g.Fgenf(w, "${%.v}", part) 364 } 365 } 366 g.Fgen(w, "`") 367 case "element": 368 g.Fgenf(w, "%.20v[%.v]", expr.Args[0], expr.Args[1]) 369 case "entries": 370 switch model.ResolveOutputs(expr.Args[0].Type()).(type) { 371 case *model.ListType, *model.TupleType: 372 if call, ok := expr.Args[0].(*model.FunctionCallExpression); ok && call.Name == "range" { 373 g.genRange(w, call, true) 374 return 375 } 376 g.Fgenf(w, "%.20v.map((k, v)", expr.Args[0]) 377 case *model.MapType, *model.ObjectType: 378 g.Fgenf(w, "Object.entries(%.v).map(([k, v])", expr.Args[0]) 379 } 380 g.Fgenf(w, " => {key: k, value: v})") 381 case "fileArchive": 382 g.Fgenf(w, "new pulumi.asset.FileArchive(%.v)", expr.Args[0]) 383 case "remoteArchive": 384 g.Fgenf(w, "new pulumi.asset.RemoteArchive(%.v)", expr.Args[0]) 385 case "assetArchive": 386 g.Fgenf(w, "new pulumi.asset.AssetArchive(%.v)", expr.Args[0]) 387 case "fileAsset": 388 g.Fgenf(w, "new pulumi.asset.FileAsset(%.v)", expr.Args[0]) 389 case "stringAsset": 390 g.Fgenf(w, "new pulumi.asset.StringAsset(%.v)", expr.Args[0]) 391 case "remoteAsset": 392 g.Fgenf(w, "new pulumi.asset.RemoteAsset(%.v)", expr.Args[0]) 393 case "filebase64": 394 g.Fgenf(w, "Buffer.from(fs.readFileSync(%v), 'binary').toString('base64')", expr.Args[0]) 395 case "filebase64sha256": 396 // Assuming the existence of the following helper method 397 g.Fgenf(w, "computeFilebase64sha256(%v)", expr.Args[0]) 398 case pcl.Invoke: 399 pkg, module, fn, diags := functionName(expr.Args[0]) 400 contract.Assert(len(diags) == 0) 401 if module != "" { 402 module = "." + module 403 } 404 isOut := pcl.IsOutputVersionInvokeCall(expr) 405 name := fmt.Sprintf("%s%s.%s", makeValidIdentifier(pkg), module, fn) 406 if isOut { 407 name = fmt.Sprintf("%sOutput", name) 408 } 409 g.Fprintf(w, "%s(", name) 410 if len(expr.Args) >= 2 { 411 g.Fgenf(w, "%.v", expr.Args[1]) 412 } 413 if len(expr.Args) == 3 { 414 g.Fgenf(w, ", %.v", expr.Args[2]) 415 } 416 g.Fprint(w, ")") 417 case "join": 418 g.Fgenf(w, "%.20v.join(%v)", expr.Args[1], expr.Args[0]) 419 case "length": 420 g.Fgenf(w, "%.20v.length", expr.Args[0]) 421 case "lookup": 422 g.Fgenf(w, "%v[%v]", expr.Args[0], expr.Args[1]) 423 if len(expr.Args) == 3 { 424 g.Fgenf(w, " || %v", expr.Args[2]) 425 } 426 case "range": 427 g.genRange(w, expr, false) 428 case "readFile": 429 g.Fgenf(w, "fs.readFileSync(%v)", expr.Args[0]) 430 case "readDir": 431 g.Fgenf(w, "fs.readDirSync(%v)", expr.Args[0]) 432 case "secret": 433 g.Fgenf(w, "pulumi.secret(%v)", expr.Args[0]) 434 case "split": 435 g.Fgenf(w, "%.20v.split(%v)", expr.Args[1], expr.Args[0]) 436 case "toBase64": 437 g.Fgenf(w, "Buffer.from(%v).toString(\"base64\")", expr.Args[0]) 438 case "fromBase64": 439 g.Fgenf(w, "Buffer.from(%v, \"base64\").toString(\"utf8\")", expr.Args[0]) 440 case "toJSON": 441 g.Fgenf(w, "JSON.stringify(%v)", expr.Args[0]) 442 case "sha1": 443 g.Fgenf(w, "crypto.createHash('sha1').update(%v).digest('hex')", expr.Args[0]) 444 case "stack": 445 g.Fgenf(w, "pulumi.getStack()") 446 case "project": 447 g.Fgenf(w, "pulumi.getProject()") 448 case "cwd": 449 g.Fgen(w, "process.cwd()") 450 451 default: 452 var rng hcl.Range 453 if expr.Syntax != nil { 454 rng = expr.Syntax.Range() 455 } 456 g.genNYI(w, "FunctionCallExpression: %v (%v)", expr.Name, rng) 457 } 458 } 459 460 func (g *generator) GenIndexExpression(w io.Writer, expr *model.IndexExpression) { 461 g.Fgenf(w, "%.20v[%.v]", expr.Collection, expr.Key) 462 } 463 464 func (g *generator) genStringLiteral(w io.Writer, v string) { 465 builder := strings.Builder{} 466 newlines := strings.Count(v, "\n") 467 if newlines == 0 || newlines == 1 && (v[0] == '\n' || v[len(v)-1] == '\n') { 468 // This string either does not contain newlines or contains a single leading or trailing newline, so we'll 469 // Generate a normal string literal. Quotes, backslashes, and newlines will be escaped in conformance with 470 // ECMA-262 11.8.4 ("String Literals"). 471 builder.WriteRune('"') 472 for _, c := range v { 473 if c == '\n' { 474 builder.WriteString(`\n`) 475 } else { 476 if c == '"' || c == '\\' { 477 builder.WriteRune('\\') 478 } 479 builder.WriteRune(c) 480 } 481 } 482 builder.WriteRune('"') 483 } else { 484 // This string does contain newlines, so we'll Generate a template string literal. "${", backquotes, and 485 // backslashes will be escaped in conformance with ECMA-262 11.8.6 ("Template Literal Lexical Components"). 486 runes := []rune(v) 487 builder.WriteRune('`') 488 for i, c := range runes { 489 switch c { 490 case '$': 491 if i < len(runes)-1 && runes[i+1] == '{' { 492 builder.WriteRune('\\') 493 } 494 case '`', '\\': 495 builder.WriteRune('\\') 496 } 497 builder.WriteRune(c) 498 } 499 builder.WriteRune('`') 500 } 501 502 g.Fgenf(w, "%s", builder.String()) 503 } 504 505 func (g *generator) GenLiteralValueExpression(w io.Writer, expr *model.LiteralValueExpression) { 506 typ := expr.Type() 507 if cns, ok := typ.(*model.ConstType); ok { 508 typ = cns.Type 509 } 510 511 switch typ { 512 case model.BoolType: 513 g.Fgenf(w, "%v", expr.Value.True()) 514 case model.NoneType: 515 g.Fgen(w, "undefined") 516 case model.NumberType: 517 bf := expr.Value.AsBigFloat() 518 if i, acc := bf.Int64(); acc == big.Exact { 519 g.Fgenf(w, "%d", i) 520 } else { 521 f, _ := bf.Float64() 522 g.Fgenf(w, "%g", f) 523 } 524 case model.StringType: 525 g.genStringLiteral(w, expr.Value.AsString()) 526 default: 527 contract.Failf("unexpected literal type in GenLiteralValueExpression: %v (%v)", expr.Type(), 528 expr.SyntaxNode().Range()) 529 } 530 } 531 532 func (g *generator) literalKey(x model.Expression) (string, bool) { 533 strKey := "" 534 switch x := x.(type) { 535 case *model.LiteralValueExpression: 536 if model.StringType.AssignableFrom(x.Type()) { 537 strKey = x.Value.AsString() 538 break 539 } 540 var buf bytes.Buffer 541 g.GenLiteralValueExpression(&buf, x) 542 return buf.String(), true 543 case *model.TemplateExpression: 544 if len(x.Parts) == 1 { 545 if lit, ok := x.Parts[0].(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) { 546 strKey = lit.Value.AsString() 547 break 548 } 549 } 550 var buf bytes.Buffer 551 g.GenTemplateExpression(&buf, x) 552 return buf.String(), true 553 default: 554 return "", false 555 } 556 557 if isLegalIdentifier(strKey) { 558 return strKey, true 559 } 560 return fmt.Sprintf("%q", strKey), true 561 562 } 563 564 func (g *generator) GenObjectConsExpression(w io.Writer, expr *model.ObjectConsExpression) { 565 if len(expr.Items) == 0 { 566 g.Fgen(w, "{}") 567 } else { 568 g.Fgen(w, "{") 569 g.Indented(func() { 570 for _, item := range expr.Items { 571 g.Fgenf(w, "\n%s", g.Indent) 572 if lit, ok := g.literalKey(item.Key); ok { 573 g.Fgenf(w, "%s", lit) 574 } else { 575 g.Fgenf(w, "[%.v]", item.Key) 576 } 577 g.Fgenf(w, ": %.v,", item.Value) 578 } 579 }) 580 g.Fgenf(w, "\n%s}", g.Indent) 581 } 582 } 583 584 func (g *generator) genRelativeTraversal(w io.Writer, traversal hcl.Traversal, parts []model.Traversable) { 585 for i, part := range traversal { 586 var key cty.Value 587 switch part := part.(type) { 588 case hcl.TraverseAttr: 589 key = cty.StringVal(part.Name) 590 case hcl.TraverseIndex: 591 key = part.Key 592 default: 593 contract.Failf("unexpected traversal part of type %T (%v)", part, part.SourceRange()) 594 } 595 596 if model.IsOptionalType(model.GetTraversableType(parts[i])) { 597 g.Fgen(w, "?") 598 } 599 600 switch key.Type() { 601 case cty.String: 602 keyVal := key.AsString() 603 if isLegalIdentifier(keyVal) { 604 g.Fgenf(w, ".%s", keyVal) 605 } else { 606 g.Fgenf(w, "[%q]", keyVal) 607 } 608 case cty.Number: 609 idx, _ := key.AsBigFloat().Int64() 610 g.Fgenf(w, "[%d]", idx) 611 default: 612 g.Fgenf(w, "[%q]", key.AsString()) 613 } 614 } 615 } 616 617 func (g *generator) GenRelativeTraversalExpression(w io.Writer, expr *model.RelativeTraversalExpression) { 618 g.Fgenf(w, "%.20v", expr.Source) 619 g.genRelativeTraversal(w, expr.Traversal, expr.Parts) 620 } 621 622 func (g *generator) GenScopeTraversalExpression(w io.Writer, expr *model.ScopeTraversalExpression) { 623 rootName := makeValidIdentifier(expr.RootName) 624 if _, ok := expr.Parts[0].(*model.SplatVariable); ok { 625 rootName = "__item" 626 } 627 628 g.Fgen(w, rootName) 629 g.genRelativeTraversal(w, expr.Traversal.SimpleSplit().Rel, expr.Parts) 630 } 631 632 func (g *generator) GenSplatExpression(w io.Writer, expr *model.SplatExpression) { 633 g.Fgenf(w, "%.20v.map(__item => %.v)", expr.Source, expr.Each) 634 } 635 636 func (g *generator) GenTemplateExpression(w io.Writer, expr *model.TemplateExpression) { 637 if len(expr.Parts) == 1 { 638 if lit, ok := expr.Parts[0].(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) { 639 g.GenLiteralValueExpression(w, lit) 640 return 641 } 642 } 643 644 g.Fgen(w, "`") 645 for _, expr := range expr.Parts { 646 if lit, ok := expr.(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) { 647 g.Fgen(w, lit.Value.AsString()) 648 } else { 649 g.Fgenf(w, "${%.v}", expr) 650 } 651 } 652 g.Fgen(w, "`") 653 } 654 655 func (g *generator) GenTemplateJoinExpression(w io.Writer, expr *model.TemplateJoinExpression) { 656 g.genNYI(w, "TemplateJoinExpression") 657 } 658 659 func (g *generator) GenTupleConsExpression(w io.Writer, expr *model.TupleConsExpression) { 660 switch len(expr.Expressions) { 661 case 0: 662 g.Fgen(w, "[]") 663 case 1: 664 g.Fgenf(w, "[%.v]", expr.Expressions[0]) 665 default: 666 g.Fgen(w, "[") 667 g.Indented(func() { 668 for _, v := range expr.Expressions { 669 g.Fgenf(w, "\n%s%.v,", g.Indent, v) 670 } 671 }) 672 g.Fgen(w, "\n", g.Indent, "]") 673 } 674 } 675 676 func (g *generator) GenUnaryOpExpression(w io.Writer, expr *model.UnaryOpExpression) { 677 opstr, precedence := "", g.GetPrecedence(expr) 678 switch expr.Operation { 679 case hclsyntax.OpLogicalNot: 680 opstr = "!" 681 case hclsyntax.OpNegate: 682 opstr = "-" 683 } 684 g.Fgenf(w, "%[2]v%.[1]*[3]v", precedence, opstr, expr.Operand) 685 }