2020-05-21 19:23:33 +02:00
|
|
|
package gen
|
|
|
|
|
|
|
|
import (
|
2020-06-06 03:52:00 +02:00
|
|
|
"bytes"
|
2020-05-21 19:23:33 +02:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"math/big"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
2021-06-24 18:17:55 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen"
|
2021-03-17 14:20:05 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
2020-05-21 19:23:33 +02:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
)
|
|
|
|
|
2020-06-30 03:10:34 +02:00
|
|
|
const keywordRange = "range"
|
|
|
|
|
2020-05-21 19:23:33 +02:00
|
|
|
func (g *generator) GetPrecedence(expr model.Expression) int {
|
|
|
|
// TODO: Current values copied from Node, update based on
|
|
|
|
// https://golang.org/ref/spec
|
|
|
|
switch expr := expr.(type) {
|
|
|
|
case *model.ConditionalExpression:
|
|
|
|
return 4
|
|
|
|
case *model.BinaryOpExpression:
|
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpLogicalOr:
|
|
|
|
return 5
|
|
|
|
case hclsyntax.OpLogicalAnd:
|
|
|
|
return 6
|
|
|
|
case hclsyntax.OpEqual, hclsyntax.OpNotEqual:
|
|
|
|
return 11
|
|
|
|
case hclsyntax.OpGreaterThan, hclsyntax.OpGreaterThanOrEqual, hclsyntax.OpLessThan,
|
|
|
|
hclsyntax.OpLessThanOrEqual:
|
|
|
|
return 12
|
|
|
|
case hclsyntax.OpAdd, hclsyntax.OpSubtract:
|
|
|
|
return 14
|
|
|
|
case hclsyntax.OpMultiply, hclsyntax.OpDivide, hclsyntax.OpModulo:
|
|
|
|
return 15
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected binary expression %v", expr)
|
|
|
|
}
|
|
|
|
case *model.UnaryOpExpression:
|
|
|
|
return 17
|
|
|
|
case *model.FunctionCallExpression:
|
|
|
|
switch expr.Name {
|
|
|
|
default:
|
|
|
|
return 20
|
|
|
|
}
|
|
|
|
case *model.ForExpression, *model.IndexExpression, *model.RelativeTraversalExpression, *model.SplatExpression,
|
|
|
|
*model.TemplateJoinExpression:
|
|
|
|
return 20
|
|
|
|
case *model.AnonymousFunctionExpression, *model.LiteralValueExpression, *model.ObjectConsExpression,
|
|
|
|
*model.ScopeTraversalExpression, *model.TemplateExpression, *model.TupleConsExpression:
|
|
|
|
return 22
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected expression %v of type %T", expr, expr)
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenAnonymousFunctionExpression generates code for an AnonymousFunctionExpression.
|
2020-05-26 16:07:54 +02:00
|
|
|
func (g *generator) GenAnonymousFunctionExpression(w io.Writer, expr *model.AnonymousFunctionExpression) {
|
2021-06-24 18:17:55 +02:00
|
|
|
g.genAnonymousFunctionExpression(w, expr, nil, false)
|
2020-06-19 02:17:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genAnonymousFunctionExpression(
|
|
|
|
w io.Writer,
|
|
|
|
expr *model.AnonymousFunctionExpression,
|
|
|
|
bodyPreamble []string,
|
2021-06-24 18:17:55 +02:00
|
|
|
inApply bool,
|
2020-06-19 02:17:51 +02:00
|
|
|
) {
|
2020-05-26 16:07:54 +02:00
|
|
|
g.Fgenf(w, "func(")
|
|
|
|
leadingSep := ""
|
|
|
|
for _, param := range expr.Signature.Parameters {
|
2020-06-06 03:52:00 +02:00
|
|
|
isInput := isInputty(param.Type)
|
2020-06-18 22:34:22 +02:00
|
|
|
g.Fgenf(w, "%s%s %s", leadingSep, param.Name, g.argumentTypeName(nil, param.Type, isInput))
|
2020-05-26 16:07:54 +02:00
|
|
|
leadingSep = ", "
|
|
|
|
}
|
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
retType := expr.Signature.ReturnType
|
|
|
|
if inApply {
|
|
|
|
retType = model.ResolveOutputs(retType)
|
|
|
|
}
|
|
|
|
|
|
|
|
retTypeName := g.argumentTypeName(nil, retType, false)
|
|
|
|
g.Fgenf(w, ") (%s, error) {\n", retTypeName)
|
2020-06-16 08:00:02 +02:00
|
|
|
|
2020-06-19 02:17:51 +02:00
|
|
|
for _, decl := range bodyPreamble {
|
|
|
|
g.Fgenf(w, "%s\n", decl)
|
|
|
|
}
|
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
body, temps := g.lowerExpression(expr.Body, retType)
|
|
|
|
g.genTempsMultiReturn(w, temps, retTypeName)
|
2020-06-16 08:00:02 +02:00
|
|
|
|
|
|
|
g.Fgenf(w, "return %v, nil", body)
|
2020-05-26 16:07:54 +02:00
|
|
|
g.Fgenf(w, "\n}")
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
|
|
|
|
2020-05-28 22:47:49 +02:00
|
|
|
func (g *generator) GenBinaryOpExpression(w io.Writer, expr *model.BinaryOpExpression) {
|
|
|
|
opstr, precedence := "", g.GetPrecedence(expr)
|
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpAdd:
|
|
|
|
opstr = "+"
|
|
|
|
case hclsyntax.OpDivide:
|
|
|
|
opstr = "/"
|
|
|
|
case hclsyntax.OpEqual:
|
|
|
|
opstr = "=="
|
|
|
|
case hclsyntax.OpGreaterThan:
|
|
|
|
opstr = ">"
|
|
|
|
case hclsyntax.OpGreaterThanOrEqual:
|
|
|
|
opstr = ">="
|
|
|
|
case hclsyntax.OpLessThan:
|
|
|
|
opstr = "<"
|
|
|
|
case hclsyntax.OpLessThanOrEqual:
|
|
|
|
opstr = "<="
|
|
|
|
case hclsyntax.OpLogicalAnd:
|
|
|
|
opstr = "&&"
|
|
|
|
case hclsyntax.OpLogicalOr:
|
|
|
|
opstr = "||"
|
|
|
|
case hclsyntax.OpModulo:
|
|
|
|
opstr = "%"
|
|
|
|
case hclsyntax.OpMultiply:
|
|
|
|
opstr = "*"
|
|
|
|
case hclsyntax.OpNotEqual:
|
|
|
|
opstr = "!="
|
|
|
|
case hclsyntax.OpSubtract:
|
|
|
|
opstr = "-"
|
|
|
|
default:
|
|
|
|
opstr, precedence = ",", 1
|
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
|
2020-05-28 22:47:49 +02:00
|
|
|
g.Fgenf(w, "%.[1]*[2]v %[3]v %.[1]*[4]o", precedence, expr.LeftOperand, opstr, expr.RightOperand)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenConditionalExpression(w io.Writer, expr *model.ConditionalExpression) {
|
|
|
|
// Ternary expressions are not supported in go so we need to allocate temp variables in the parent scope.
|
|
|
|
// This is handled by lower expression and rewriteTernaries
|
|
|
|
contract.Failf("unlowered conditional expression @ %v", expr.SyntaxNode().Range())
|
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
|
|
|
|
// GenForExpression generates code for a ForExpression.
|
|
|
|
func (g *generator) GenForExpression(w io.Writer, expr *model.ForExpression) { /*TODO*/ }
|
|
|
|
|
|
|
|
func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionCallExpression) {
|
2021-06-24 18:17:55 +02:00
|
|
|
//nolint:goconst
|
2020-05-21 19:23:33 +02:00
|
|
|
switch expr.Name {
|
|
|
|
case hcl2.IntrinsicConvert:
|
|
|
|
switch arg := expr.Args[0].(type) {
|
|
|
|
case *model.TupleConsExpression:
|
|
|
|
g.genTupleConsExpression(w, arg, expr.Type())
|
|
|
|
case *model.ObjectConsExpression:
|
2020-06-18 22:34:22 +02:00
|
|
|
isInput := false
|
|
|
|
g.genObjectConsExpression(w, arg, expr.Type(), isInput)
|
2020-06-06 03:52:00 +02:00
|
|
|
case *model.LiteralValueExpression:
|
|
|
|
g.genLiteralValueExpression(w, arg, expr.Type())
|
2021-06-24 18:17:55 +02:00
|
|
|
case *model.TemplateExpression:
|
|
|
|
g.genTemplateExpression(w, arg, expr.Type())
|
|
|
|
case *model.ScopeTraversalExpression:
|
|
|
|
g.genScopeTraversalExpression(w, arg, expr.Type())
|
2020-05-21 19:23:33 +02:00
|
|
|
default:
|
2021-06-24 18:17:55 +02:00
|
|
|
g.Fgenf(w, "%.v", expr.Args[0])
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
|
|
|
case hcl2.IntrinsicApply:
|
2020-05-26 16:07:54 +02:00
|
|
|
g.genApply(w, expr)
|
2020-05-21 19:23:33 +02:00
|
|
|
case "element":
|
|
|
|
g.genNYI(w, "element")
|
|
|
|
case "entries":
|
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
// switch model.ResolveOutputs(expr.Args[0].Type()).(type) {
|
|
|
|
// case *model.ListType, *model.TupleType:
|
|
|
|
// if call, ok := expr.Args[0].(*model.FunctionCallExpression); ok && call.Name == "range" {
|
|
|
|
// g.genRange(w, call, true)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// g.Fgenf(w, "%.20v.Select((v, k)", expr.Args[0])
|
|
|
|
// case *model.MapType, *model.ObjectType:
|
|
|
|
// g.genNYI(w, "MapOrObjectEntries")
|
|
|
|
// }
|
|
|
|
// g.Fgenf(w, " => new { Key = k, Value = v })")
|
|
|
|
case "fileArchive":
|
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
// g.Fgenf(w, "new FileArchive(%.v)", expr.Args[0])
|
|
|
|
case "fileAsset":
|
2020-06-16 08:00:02 +02:00
|
|
|
g.Fgenf(w, "pulumi.NewFileAsset(%.v)", expr.Args[0])
|
2020-05-21 19:23:33 +02:00
|
|
|
case hcl2.Invoke:
|
2020-06-18 22:34:22 +02:00
|
|
|
pkg, module, fn, diags := g.functionName(expr.Args[0])
|
2020-06-06 03:52:00 +02:00
|
|
|
contract.Assert(len(diags) == 0)
|
2020-06-18 02:19:39 +02:00
|
|
|
if module == "" {
|
|
|
|
module = pkg
|
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
name := fmt.Sprintf("%s.%s", module, fn)
|
|
|
|
|
|
|
|
optionsBag := ""
|
|
|
|
var buf bytes.Buffer
|
|
|
|
if len(expr.Args) == 3 {
|
|
|
|
g.Fgenf(&buf, ", %.v", expr.Args[2])
|
|
|
|
} else {
|
|
|
|
g.Fgenf(&buf, ", nil")
|
|
|
|
}
|
|
|
|
optionsBag = buf.String()
|
2020-05-21 19:23:33 +02:00
|
|
|
|
2020-06-06 03:52:00 +02:00
|
|
|
g.Fgenf(w, "%s(ctx, ", name)
|
|
|
|
g.Fgenf(w, "%.v", expr.Args[1])
|
|
|
|
g.Fgenf(w, "%v)", optionsBag)
|
2020-05-21 19:23:33 +02:00
|
|
|
case "length":
|
2020-06-18 02:19:39 +02:00
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
// g.Fgenf(w, "%.20v.Length", expr.Args[0])
|
2020-05-21 19:23:33 +02:00
|
|
|
case "lookup":
|
|
|
|
g.genNYI(w, "Lookup")
|
2020-06-30 03:10:34 +02:00
|
|
|
case keywordRange:
|
2020-05-21 19:23:33 +02:00
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
// g.genRange(w, expr, false)
|
|
|
|
case "readFile":
|
|
|
|
g.genNYI(w, "ReadFile")
|
|
|
|
case "readDir":
|
2021-06-24 18:17:55 +02:00
|
|
|
contract.Failf("unlowered readDir function expression @ %v", expr.SyntaxNode().Range())
|
2020-06-30 20:35:24 +02:00
|
|
|
case "secret":
|
|
|
|
outputTypeName := "pulumi.Any"
|
|
|
|
if model.ResolveOutputs(expr.Type()) != model.DynamicType {
|
|
|
|
outputTypeName = g.argumentTypeName(nil, expr.Type(), false)
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "pulumi.ToSecret(%v).(%sOutput)", expr.Args[0], outputTypeName)
|
2020-05-21 19:23:33 +02:00
|
|
|
case "split":
|
2020-06-18 02:19:39 +02:00
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
// g.Fgenf(w, "%.20v.Split(%v)", expr.Args[1], expr.Args[0])
|
2020-05-21 19:23:33 +02:00
|
|
|
case "toJSON":
|
2020-06-06 03:52:00 +02:00
|
|
|
contract.Failf("unlowered toJSON function expression @ %v", expr.SyntaxNode().Range())
|
2020-06-16 08:00:02 +02:00
|
|
|
case "mimeType":
|
|
|
|
g.Fgenf(w, "mime.TypeByExtension(path.Ext(%.v))", expr.Args[0])
|
2020-05-21 19:23:33 +02:00
|
|
|
default:
|
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-18 02:19:39 +02:00
|
|
|
func (g *generator) GenIndexExpression(w io.Writer, expr *model.IndexExpression) {
|
|
|
|
g.Fgenf(w, "%.20v[%.v]", expr.Collection, expr.Key)
|
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
|
|
|
|
func (g *generator) GenLiteralValueExpression(w io.Writer, expr *model.LiteralValueExpression) {
|
2020-06-06 03:52:00 +02:00
|
|
|
g.genLiteralValueExpression(w, expr, expr.Type())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genLiteralValueExpression(w io.Writer, expr *model.LiteralValueExpression, destType model.Type) {
|
2021-06-24 18:17:55 +02:00
|
|
|
exprType := expr.Type()
|
|
|
|
if cns, ok := exprType.(*model.ConstType); ok {
|
|
|
|
exprType = cns.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
if exprType == model.NoneType {
|
2020-08-07 04:09:18 +02:00
|
|
|
g.Fgen(w, "nil")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-18 22:34:22 +02:00
|
|
|
argTypeName := g.argumentTypeName(expr, destType, false)
|
2020-06-06 03:52:00 +02:00
|
|
|
isPulumiType := strings.HasPrefix(argTypeName, "pulumi.")
|
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
switch exprType {
|
|
|
|
case model.BoolType:
|
|
|
|
if isPulumiType {
|
|
|
|
g.Fgenf(w, "%s(%v)", argTypeName, expr.Value.True())
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%v", expr.Value.True())
|
|
|
|
}
|
|
|
|
case model.NumberType, model.IntType:
|
|
|
|
bf := expr.Value.AsBigFloat()
|
|
|
|
if i, acc := bf.Int64(); acc == big.Exact {
|
2020-06-06 03:52:00 +02:00
|
|
|
if isPulumiType {
|
2021-06-24 18:17:55 +02:00
|
|
|
g.Fgenf(w, "%s(%d)", argTypeName, i)
|
2020-06-06 03:52:00 +02:00
|
|
|
} else {
|
2021-06-24 18:17:55 +02:00
|
|
|
g.Fgenf(w, "%d", i)
|
2020-06-06 03:52:00 +02:00
|
|
|
}
|
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
} else {
|
|
|
|
f, _ := bf.Float64()
|
2020-06-06 03:52:00 +02:00
|
|
|
if isPulumiType {
|
2021-06-24 18:17:55 +02:00
|
|
|
g.Fgenf(w, "%s(%g)", argTypeName, f)
|
2020-06-06 03:52:00 +02:00
|
|
|
} else {
|
2021-06-24 18:17:55 +02:00
|
|
|
g.Fgenf(w, "%g", f)
|
2020-06-06 03:52:00 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-24 18:17:55 +02:00
|
|
|
case model.StringType:
|
|
|
|
strVal := expr.Value.AsString()
|
|
|
|
if isPulumiType {
|
|
|
|
g.Fgenf(w, "%s(", argTypeName)
|
|
|
|
g.genStringLiteral(w, strVal)
|
|
|
|
g.Fgenf(w, ")")
|
|
|
|
} else {
|
|
|
|
g.genStringLiteral(w, strVal)
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
|
|
|
default:
|
2021-06-24 18:17:55 +02:00
|
|
|
contract.Failf("unexpected opaque type in GenLiteralValueExpression: %v (%v)", destType,
|
2020-05-21 19:23:33 +02:00
|
|
|
expr.SyntaxNode().Range())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenObjectConsExpression(w io.Writer, expr *model.ObjectConsExpression) {
|
2020-06-18 22:34:22 +02:00
|
|
|
isInput := false
|
|
|
|
g.genObjectConsExpression(w, expr, expr.Type(), isInput)
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
|
|
|
|
2020-06-18 22:34:22 +02:00
|
|
|
func (g *generator) genObjectConsExpression(
|
|
|
|
w io.Writer,
|
|
|
|
expr *model.ObjectConsExpression,
|
|
|
|
destType model.Type,
|
|
|
|
isInput bool,
|
|
|
|
) {
|
2021-06-24 18:17:55 +02:00
|
|
|
if len(expr.Items) == 0 {
|
|
|
|
g.Fgenf(w, "nil")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var temps []interface{}
|
|
|
|
isInput = isInput || isInputty(destType)
|
|
|
|
typeName := g.argumentTypeName(expr, destType, isInput)
|
|
|
|
if schemaType, ok := hcl2.GetSchemaForType(destType); ok {
|
|
|
|
if obj, ok := codegen.UnwrapType(schemaType).(*schema.ObjectType); ok {
|
|
|
|
if g.useLookupInvokeForm(obj.Token) {
|
|
|
|
typeName = strings.Replace(typeName, ".Get", ".Lookup", 1)
|
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
}
|
2021-06-24 18:17:55 +02:00
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
// TODO: @pgavlin --- ineffectual assignment, was there some work in flight here?
|
|
|
|
// if strings.HasSuffix(typeName, "Args") {
|
|
|
|
// isInput = true
|
|
|
|
// }
|
|
|
|
// // invokes are not inputty
|
|
|
|
// if strings.Contains(typeName, ".Lookup") || strings.Contains(typeName, ".Get") {
|
|
|
|
// isInput = false
|
|
|
|
// }
|
|
|
|
isMap := strings.HasPrefix(typeName, "map[")
|
|
|
|
|
|
|
|
// TODO: retrieve schema and propagate optionals to emit bool ptr, etc.
|
|
|
|
|
|
|
|
// first lower all inner expressions and emit temps
|
|
|
|
for i, item := range expr.Items {
|
|
|
|
// don't treat keys as inputs
|
|
|
|
k, kTemps := g.lowerExpression(item.Key, item.Key.Type())
|
|
|
|
temps = append(temps, kTemps...)
|
|
|
|
item.Key = k
|
|
|
|
x, xTemps := g.lowerExpression(item.Value, item.Value.Type())
|
|
|
|
temps = append(temps, xTemps...)
|
|
|
|
item.Value = x
|
|
|
|
expr.Items[i] = item
|
|
|
|
}
|
|
|
|
g.genTemps(w, temps)
|
|
|
|
|
|
|
|
if isMap || !strings.HasSuffix(typeName, "Args") {
|
|
|
|
g.Fgenf(w, "%s", typeName)
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "&%s", typeName)
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "{\n")
|
|
|
|
|
|
|
|
for _, item := range expr.Items {
|
|
|
|
if lit, ok := g.literalKey(item.Key); ok {
|
|
|
|
if isMap || strings.HasSuffix(typeName, "Map") {
|
|
|
|
g.Fgenf(w, "\"%s\"", lit)
|
2020-06-06 03:52:00 +02:00
|
|
|
} else {
|
2021-06-24 18:17:55 +02:00
|
|
|
g.Fgenf(w, "%s", Title(lit))
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
2021-06-24 18:17:55 +02:00
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%.v", item.Key)
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
g.Fgenf(w, ": %.v,\n", item.Value)
|
2020-06-18 02:19:39 +02:00
|
|
|
}
|
2021-06-24 18:17:55 +02:00
|
|
|
|
|
|
|
g.Fgenf(w, "}")
|
2020-06-18 02:19:39 +02:00
|
|
|
}
|
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
func (g *generator) genRelativeTraversalExpression(
|
|
|
|
w io.Writer, expr *model.RelativeTraversalExpression, isInput bool) {
|
2020-06-18 02:19:39 +02:00
|
|
|
|
|
|
|
if _, ok := expr.Parts[0].(*model.PromiseType); ok {
|
|
|
|
isInput = false
|
|
|
|
}
|
|
|
|
if _, ok := expr.Parts[0].(*hcl2.Resource); ok {
|
|
|
|
isInput = false
|
|
|
|
}
|
|
|
|
if isInput {
|
2020-06-18 22:34:22 +02:00
|
|
|
g.Fgenf(w, "%s(", g.argumentTypeName(expr, expr.Type(), isInput))
|
2020-06-18 02:19:39 +02:00
|
|
|
}
|
|
|
|
g.GenRelativeTraversalExpression(w, expr)
|
|
|
|
if isInput {
|
|
|
|
g.Fgenf(w, ")")
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenRelativeTraversalExpression(w io.Writer, expr *model.RelativeTraversalExpression) {
|
|
|
|
g.Fgenf(w, "%.20v", expr.Source)
|
2020-06-18 02:19:39 +02:00
|
|
|
isRootResource := false
|
|
|
|
if ie, ok := expr.Source.(*model.IndexExpression); ok {
|
|
|
|
if se, ok := ie.Collection.(*model.ScopeTraversalExpression); ok {
|
|
|
|
if _, ok := se.Parts[0].(*hcl2.Resource); ok {
|
|
|
|
isRootResource = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.genRelativeTraversal(w, expr.Traversal, expr.Parts, isRootResource)
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenScopeTraversalExpression(w io.Writer, expr *model.ScopeTraversalExpression) {
|
2021-06-24 18:17:55 +02:00
|
|
|
g.genScopeTraversalExpression(w, expr, expr.Type())
|
2020-06-06 03:52:00 +02:00
|
|
|
}
|
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
func (g *generator) genScopeTraversalExpression(
|
|
|
|
w io.Writer, expr *model.ScopeTraversalExpression, destType model.Type) {
|
2020-05-21 19:23:33 +02:00
|
|
|
rootName := expr.RootName
|
2020-06-18 02:19:39 +02:00
|
|
|
|
|
|
|
if _, ok := expr.Parts[0].(*model.SplatVariable); ok {
|
|
|
|
rootName = "val0"
|
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
|
2020-06-06 03:52:00 +02:00
|
|
|
genIDCall := false
|
2020-05-21 19:23:33 +02:00
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
isInput := false
|
|
|
|
if schemaType, ok := hcl2.GetSchemaForType(destType); ok {
|
|
|
|
_, isInput = schemaType.(*schema.InputType)
|
|
|
|
}
|
|
|
|
|
2020-05-21 19:23:33 +02:00
|
|
|
if resource, ok := expr.Parts[0].(*hcl2.Resource); ok {
|
2020-06-06 03:52:00 +02:00
|
|
|
isInput = false
|
2020-06-18 02:19:39 +02:00
|
|
|
if _, ok := hcl2.GetSchemaForType(resource.InputType); ok {
|
2020-06-06 03:52:00 +02:00
|
|
|
// convert .id into .ID()
|
|
|
|
last := expr.Traversal[len(expr.Traversal)-1]
|
|
|
|
if attr, ok := last.(hcl.TraverseAttr); ok && attr.Name == "id" {
|
|
|
|
genIDCall = true
|
|
|
|
expr.Traversal = expr.Traversal[:len(expr.Traversal)-1]
|
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
|
|
|
|
// TODO if it's an array type, we need a lowering step to turn []string -> pulumi.StringArray
|
|
|
|
if isInput {
|
2020-06-24 20:07:26 +02:00
|
|
|
argType := g.argumentTypeName(expr, expr.Type(), isInput)
|
|
|
|
if strings.HasSuffix(argType, "Array") {
|
2021-06-24 18:17:55 +02:00
|
|
|
destTypeName := g.argumentTypeName(expr, destType, isInput)
|
|
|
|
if argType != destTypeName {
|
|
|
|
// use a helper to transform prompt arrays into inputty arrays
|
|
|
|
var helper *promptToInputArrayHelper
|
|
|
|
if h, ok := g.arrayHelpers[argType]; ok {
|
|
|
|
helper = h
|
|
|
|
} else {
|
|
|
|
// helpers are emitted at the end in the postamble step
|
|
|
|
helper = &promptToInputArrayHelper{
|
|
|
|
destType: argType,
|
|
|
|
}
|
|
|
|
g.arrayHelpers[argType] = helper
|
2020-06-24 20:07:26 +02:00
|
|
|
}
|
2021-06-24 18:17:55 +02:00
|
|
|
g.Fgenf(w, "%s(", helper.getFnName())
|
|
|
|
defer g.Fgenf(w, ")")
|
2020-06-24 20:07:26 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%s(", g.argumentTypeName(expr, expr.Type(), isInput))
|
2021-06-24 18:17:55 +02:00
|
|
|
defer g.Fgenf(w, ")")
|
2020-06-24 20:07:26 +02:00
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
}
|
2020-06-16 08:00:02 +02:00
|
|
|
|
2020-06-18 02:19:39 +02:00
|
|
|
// TODO: this isn't exhaustively correct as "range" could be a legit var name
|
|
|
|
// instead we should probably use a fn call expression here for entries/range
|
|
|
|
// similar to other languages
|
2020-06-30 03:10:34 +02:00
|
|
|
if rootName == keywordRange {
|
2020-06-16 08:00:02 +02:00
|
|
|
part := expr.Traversal[1].(hcl.TraverseAttr).Name
|
|
|
|
switch part {
|
|
|
|
case "value":
|
|
|
|
g.Fgenf(w, "val0")
|
|
|
|
case "key":
|
|
|
|
g.Fgenf(w, "key0")
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected traversal on range expression: %s", part)
|
|
|
|
}
|
|
|
|
} else {
|
2020-06-30 03:10:34 +02:00
|
|
|
g.Fgen(w, makeValidIdentifier(rootName))
|
2020-06-18 02:19:39 +02:00
|
|
|
isRootResource := false
|
|
|
|
g.genRelativeTraversal(w, expr.Traversal.SimpleSplit().Rel, expr.Parts[1:], isRootResource)
|
2020-06-16 08:00:02 +02:00
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
|
|
|
|
if genIDCall {
|
|
|
|
g.Fgenf(w, ".ID()")
|
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// GenSplatExpression generates code for a SplatExpression.
|
2020-06-18 02:19:39 +02:00
|
|
|
func (g *generator) GenSplatExpression(w io.Writer, expr *model.SplatExpression) {
|
|
|
|
contract.Failf("unlowered splat expression @ %v", expr.SyntaxNode().Range())
|
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
|
|
|
|
// GenTemplateExpression generates code for a TemplateExpression.
|
2020-05-27 16:32:13 +02:00
|
|
|
func (g *generator) GenTemplateExpression(w io.Writer, expr *model.TemplateExpression) {
|
2021-06-24 18:17:55 +02:00
|
|
|
g.genTemplateExpression(w, expr, expr.Type())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genTemplateExpression(w io.Writer, expr *model.TemplateExpression, destType model.Type) {
|
2020-05-27 16:32:13 +02:00
|
|
|
if len(expr.Parts) == 1 {
|
2021-06-24 18:17:55 +02:00
|
|
|
if lit, ok := expr.Parts[0].(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) {
|
|
|
|
g.genLiteralValueExpression(w, lit, destType)
|
2020-05-27 16:32:13 +02:00
|
|
|
return
|
|
|
|
}
|
2020-06-16 08:00:02 +02:00
|
|
|
} else {
|
2021-06-24 18:17:55 +02:00
|
|
|
argTypeName := g.argumentTypeName(expr, destType, false)
|
|
|
|
isPulumiType := strings.HasPrefix(argTypeName, "pulumi.")
|
|
|
|
if isPulumiType {
|
|
|
|
g.Fgenf(w, "%s(", argTypeName)
|
|
|
|
defer g.Fgenf(w, ")")
|
|
|
|
}
|
|
|
|
|
2020-06-16 08:00:02 +02:00
|
|
|
fmtMaker := make([]string, len(expr.Parts)+1)
|
|
|
|
fmtStr := strings.Join(fmtMaker, "%v")
|
|
|
|
g.Fgenf(w, "fmt.Sprintf(\"%s\"", fmtStr)
|
|
|
|
for _, v := range expr.Parts {
|
|
|
|
g.Fgenf(w, ", %.v", v)
|
|
|
|
}
|
|
|
|
g.Fgenf(w, ")")
|
2020-05-27 16:32:13 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
|
|
|
|
// GenTemplateJoinExpression generates code for a TemplateJoinExpression.
|
|
|
|
func (g *generator) GenTemplateJoinExpression(w io.Writer, expr *model.TemplateJoinExpression) { /*TODO*/
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenTupleConsExpression(w io.Writer, expr *model.TupleConsExpression) {
|
|
|
|
g.genTupleConsExpression(w, expr, expr.Type())
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenTupleConsExpression generates code for a TupleConsExpression.
|
|
|
|
func (g *generator) genTupleConsExpression(w io.Writer, expr *model.TupleConsExpression, destType model.Type) {
|
2021-06-24 18:17:55 +02:00
|
|
|
isInput := isInputty(destType)
|
2020-06-06 03:52:00 +02:00
|
|
|
|
|
|
|
var temps []interface{}
|
|
|
|
for i, item := range expr.Expressions {
|
2021-06-24 18:17:55 +02:00
|
|
|
item, itemTemps := g.lowerExpression(item, item.Type())
|
2020-06-06 03:52:00 +02:00
|
|
|
temps = append(temps, itemTemps...)
|
|
|
|
expr.Expressions[i] = item
|
|
|
|
}
|
|
|
|
g.genTemps(w, temps)
|
2020-06-18 22:34:22 +02:00
|
|
|
argType := g.argumentTypeName(expr, destType, isInput)
|
2020-05-21 19:23:33 +02:00
|
|
|
g.Fgenf(w, "%s{\n", argType)
|
|
|
|
switch len(expr.Expressions) {
|
|
|
|
case 0:
|
|
|
|
// empty array
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
for _, v := range expr.Expressions {
|
|
|
|
g.Fgenf(w, "%v,\n", v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "}")
|
|
|
|
}
|
|
|
|
|
2020-05-28 22:47:49 +02:00
|
|
|
func (g *generator) GenUnaryOpExpression(w io.Writer, expr *model.UnaryOpExpression) {
|
|
|
|
opstr, precedence := "", g.GetPrecedence(expr)
|
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpLogicalNot:
|
|
|
|
opstr = "!"
|
|
|
|
case hclsyntax.OpNegate:
|
|
|
|
opstr = "-"
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "%[2]v%.[1]*[3]v", precedence, opstr, expr.Operand)
|
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
var typeNameID = 0
|
|
|
|
|
2020-05-21 19:23:33 +02:00
|
|
|
// argumentTypeName computes the go type for the given expression and model type.
|
2021-06-24 18:17:55 +02:00
|
|
|
func (g *generator) argumentTypeName(expr model.Expression, destType model.Type, isInput bool) (result string) {
|
|
|
|
// defer func(id int, t model.Type) {
|
|
|
|
// schemaType, _ := hcl2.GetSchemaForType(destType)
|
|
|
|
// log.Printf("%v: argumentTypeName(%v, %v, %v) = %v", id, t, isInput, schemaType, result)
|
|
|
|
// }(typeNameID, destType)
|
|
|
|
typeNameID++
|
|
|
|
|
|
|
|
if cns, ok := destType.(*model.ConstType); ok {
|
|
|
|
destType = cns.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
// This can happen with null literals.
|
|
|
|
if destType == model.NoneType {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
if schemaType, ok := hcl2.GetSchemaForType(destType); ok {
|
|
|
|
pkg := &pkgContext{pkg: &schema.Package{Name: "main"}}
|
|
|
|
return pkg.argsType(schemaType)
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
|
|
|
|
2020-05-28 22:47:49 +02:00
|
|
|
switch destType := destType.(type) {
|
2020-05-26 16:07:54 +02:00
|
|
|
case *model.OpaqueType:
|
2020-05-28 22:47:49 +02:00
|
|
|
switch destType {
|
2020-06-06 03:52:00 +02:00
|
|
|
case model.IntType:
|
|
|
|
if isInput {
|
|
|
|
return "pulumi.Int"
|
|
|
|
}
|
|
|
|
return "int"
|
2020-05-28 22:47:49 +02:00
|
|
|
case model.NumberType:
|
2020-06-06 03:52:00 +02:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.Float64"
|
|
|
|
}
|
2020-05-28 22:47:49 +02:00
|
|
|
return "float64"
|
2020-06-30 20:35:24 +02:00
|
|
|
case model.StringType:
|
2020-06-06 03:52:00 +02:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.String"
|
|
|
|
}
|
|
|
|
return "string"
|
|
|
|
case model.BoolType:
|
|
|
|
if isInput {
|
|
|
|
return "pulumi.Bool"
|
|
|
|
}
|
|
|
|
return "bool"
|
2020-06-30 20:35:24 +02:00
|
|
|
case model.DynamicType:
|
|
|
|
if isInput {
|
|
|
|
return "pulumi.Any"
|
|
|
|
}
|
|
|
|
return "interface{}"
|
2020-05-28 22:47:49 +02:00
|
|
|
default:
|
|
|
|
return destType.Name
|
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
case *model.ObjectType:
|
2020-06-30 00:29:41 +02:00
|
|
|
|
2020-06-18 02:19:39 +02:00
|
|
|
if isInput {
|
2020-06-30 00:29:41 +02:00
|
|
|
// check for element type uniformity and return appropriate type if so
|
|
|
|
allSameType := true
|
|
|
|
var elmType string
|
|
|
|
for _, v := range destType.Properties {
|
|
|
|
valType := g.argumentTypeName(nil, v, true)
|
|
|
|
if elmType != "" && elmType != valType {
|
|
|
|
allSameType = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
elmType = valType
|
|
|
|
}
|
|
|
|
if allSameType && elmType != "" {
|
|
|
|
return fmt.Sprintf("%sMap", elmType)
|
|
|
|
}
|
2020-06-18 02:19:39 +02:00
|
|
|
return "pulumi.Map"
|
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
return "map[string]interface{}"
|
|
|
|
case *model.MapType:
|
2020-06-18 22:34:22 +02:00
|
|
|
valType := g.argumentTypeName(nil, destType.ElementType, isInput)
|
2020-06-18 02:19:39 +02:00
|
|
|
if isInput {
|
|
|
|
return fmt.Sprintf("pulumi.%sMap", Title(valType))
|
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
return fmt.Sprintf("map[string]%s", valType)
|
|
|
|
case *model.ListType:
|
2020-06-18 22:34:22 +02:00
|
|
|
argTypeName := g.argumentTypeName(nil, destType.ElementType, isInput)
|
2020-06-30 01:33:52 +02:00
|
|
|
if strings.HasPrefix(argTypeName, "pulumi.") && argTypeName != "pulumi.Resource" {
|
2020-06-06 03:52:00 +02:00
|
|
|
return fmt.Sprintf("%sArray", argTypeName)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("[]%s", argTypeName)
|
|
|
|
case *model.TupleType:
|
|
|
|
// attempt to collapse tuple types. intentionally does not use model.UnifyTypes
|
|
|
|
// correct go code requires all types to match, or use of interface{}
|
|
|
|
var elmType model.Type
|
|
|
|
for i, t := range destType.ElementTypes {
|
|
|
|
if i == 0 {
|
|
|
|
elmType = t
|
2021-06-24 18:17:55 +02:00
|
|
|
if cns, ok := elmType.(*model.ConstType); ok {
|
|
|
|
elmType = cns.Type
|
|
|
|
}
|
|
|
|
continue
|
2020-06-06 03:52:00 +02:00
|
|
|
}
|
|
|
|
|
2021-06-24 18:17:55 +02:00
|
|
|
if !elmType.AssignableFrom(t) {
|
2020-06-06 03:52:00 +02:00
|
|
|
elmType = nil
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if elmType != nil {
|
2020-06-18 22:34:22 +02:00
|
|
|
argTypeName := g.argumentTypeName(nil, elmType, isInput)
|
2020-06-30 01:33:52 +02:00
|
|
|
if strings.HasPrefix(argTypeName, "pulumi.") && argTypeName != "pulumi.Resource" {
|
2020-06-06 03:52:00 +02:00
|
|
|
return fmt.Sprintf("%sArray", argTypeName)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("[]%s", argTypeName)
|
|
|
|
}
|
|
|
|
|
|
|
|
if isInput {
|
|
|
|
return "pulumi.Array"
|
|
|
|
}
|
|
|
|
return "[]interface{}"
|
|
|
|
case *model.OutputType:
|
|
|
|
isInput = true
|
2020-06-18 22:34:22 +02:00
|
|
|
return g.argumentTypeName(expr, destType.ElementType, isInput)
|
2020-06-06 03:52:00 +02:00
|
|
|
case *model.UnionType:
|
|
|
|
for _, ut := range destType.ElementTypes {
|
2021-06-24 18:17:55 +02:00
|
|
|
switch ut := ut.(type) {
|
|
|
|
case *model.OpaqueType:
|
|
|
|
return g.argumentTypeName(expr, ut, isInput)
|
|
|
|
case *model.ConstType:
|
|
|
|
return g.argumentTypeName(expr, ut.Type, isInput)
|
|
|
|
case *model.TupleType:
|
2020-06-18 22:34:22 +02:00
|
|
|
return g.argumentTypeName(expr, ut, isInput)
|
2021-04-16 14:04:42 +02:00
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
}
|
2020-05-28 22:47:49 +02:00
|
|
|
return "interface{}"
|
2020-06-06 03:52:00 +02:00
|
|
|
case *model.PromiseType:
|
2020-06-18 22:34:22 +02:00
|
|
|
return g.argumentTypeName(expr, destType.ElementType, isInput)
|
2020-05-26 16:07:54 +02:00
|
|
|
default:
|
2020-06-06 03:52:00 +02:00
|
|
|
contract.Failf("unexpected destType type %T", destType)
|
2020-05-26 16:07:54 +02:00
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genRelativeTraversal(w io.Writer,
|
2020-06-18 02:19:39 +02:00
|
|
|
traversal hcl.Traversal, parts []model.Traversable, isRootResource bool) {
|
2020-05-21 19:23:33 +02:00
|
|
|
|
2020-06-18 02:19:39 +02:00
|
|
|
for i, part := range traversal {
|
2020-05-21 19:23:33 +02:00
|
|
|
var key cty.Value
|
|
|
|
switch part := part.(type) {
|
|
|
|
case hcl.TraverseAttr:
|
|
|
|
key = cty.StringVal(part.Name)
|
|
|
|
case hcl.TraverseIndex:
|
|
|
|
key = part.Key
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected traversal part of type %T (%v)", part, part.SourceRange())
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO handle optionals in go
|
|
|
|
// if model.IsOptionalType(model.GetTraversableType(parts[i])) {
|
|
|
|
// g.Fgen(w, "?")
|
|
|
|
// }
|
|
|
|
|
|
|
|
switch key.Type() {
|
|
|
|
case cty.String:
|
2020-06-18 02:19:39 +02:00
|
|
|
shouldConvert := isRootResource
|
|
|
|
if _, ok := parts[i].(*model.OutputType); ok {
|
|
|
|
shouldConvert = true
|
|
|
|
}
|
|
|
|
if key.AsString() == "id" && shouldConvert {
|
|
|
|
g.Fgenf(w, ".ID()")
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, ".%s", Title(key.AsString()))
|
|
|
|
}
|
2020-05-21 19:23:33 +02:00
|
|
|
case cty.Number:
|
|
|
|
idx, _ := key.AsBigFloat().Int64()
|
|
|
|
g.Fgenf(w, "[%d]", idx)
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected traversal key of type %T (%v)", key, key.AsString())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type nameInfo int
|
|
|
|
|
|
|
|
func (nameInfo) Format(name string) string {
|
|
|
|
// TODO
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
2020-06-18 02:19:39 +02:00
|
|
|
// lowerExpression amends the expression with intrinsics for Go generation.
|
2021-06-24 18:17:55 +02:00
|
|
|
func (g *generator) lowerExpression(expr model.Expression, typ model.Type) (
|
2020-06-06 03:52:00 +02:00
|
|
|
model.Expression, []interface{}) {
|
2020-06-30 01:33:52 +02:00
|
|
|
expr = hcl2.RewritePropertyReferences(expr)
|
2020-05-21 19:23:33 +02:00
|
|
|
expr, diags := hcl2.RewriteApplies(expr, nameInfo(0), false /*TODO*/)
|
|
|
|
expr = hcl2.RewriteConversions(expr, typ)
|
2020-06-16 08:00:02 +02:00
|
|
|
expr, tTemps, ternDiags := g.rewriteTernaries(expr, g.ternaryTempSpiller)
|
2021-06-24 18:17:55 +02:00
|
|
|
expr, jTemps, jsonDiags := g.rewriteToJSON(expr)
|
2020-06-16 08:00:02 +02:00
|
|
|
expr, rTemps, readDirDiags := g.rewriteReadDir(expr, g.readDirTempSpiller)
|
2020-06-18 02:19:39 +02:00
|
|
|
expr, sTemps, splatDiags := g.rewriteSplat(expr, g.splatSpiller)
|
2020-06-23 19:59:26 +02:00
|
|
|
expr, oTemps, optDiags := g.rewriteOptionals(expr, g.optionalSpiller)
|
2020-06-06 03:52:00 +02:00
|
|
|
|
|
|
|
var temps []interface{}
|
|
|
|
for _, t := range tTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
|
|
|
for _, t := range jTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-06-16 08:00:02 +02:00
|
|
|
for _, t := range rTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-06-18 02:19:39 +02:00
|
|
|
for _, t := range sTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-06-23 19:59:26 +02:00
|
|
|
for _, t := range oTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-05-28 22:47:49 +02:00
|
|
|
diags = append(diags, ternDiags...)
|
2020-06-06 03:52:00 +02:00
|
|
|
diags = append(diags, jsonDiags...)
|
2020-06-16 08:00:02 +02:00
|
|
|
diags = append(diags, readDirDiags...)
|
2020-06-18 02:19:39 +02:00
|
|
|
diags = append(diags, splatDiags...)
|
2020-06-23 19:59:26 +02:00
|
|
|
diags = append(diags, optDiags...)
|
2020-05-28 22:47:49 +02:00
|
|
|
contract.Assert(len(diags) == 0)
|
|
|
|
return expr, temps
|
2020-05-21 19:23:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genNYI(w io.Writer, reason string, vs ...interface{}) {
|
2020-06-10 19:21:53 +02:00
|
|
|
message := fmt.Sprintf("not yet implemented: %s", fmt.Sprintf(reason, vs...))
|
|
|
|
g.diagnostics = append(g.diagnostics, &hcl.Diagnostic{
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: message,
|
|
|
|
Detail: message,
|
|
|
|
})
|
2020-05-21 19:23:33 +02:00
|
|
|
g.Fgenf(w, "\"TODO: %s\"", fmt.Sprintf(reason, vs...))
|
|
|
|
}
|
2020-05-26 16:07:54 +02:00
|
|
|
|
|
|
|
func (g *generator) genApply(w io.Writer, expr *model.FunctionCallExpression) {
|
|
|
|
// Extract the list of outputs and the continuation expression from the `__apply` arguments.
|
|
|
|
applyArgs, then := hcl2.ParseApplyCall(expr)
|
|
|
|
isInput := false
|
2020-06-18 22:34:22 +02:00
|
|
|
retType := g.argumentTypeName(nil, then.Signature.ReturnType, isInput)
|
2020-05-26 16:07:54 +02:00
|
|
|
// TODO account for outputs in other namespaces like aws
|
2020-06-16 08:00:02 +02:00
|
|
|
typeAssertion := fmt.Sprintf(".(%sOutput)", retType)
|
|
|
|
if !strings.HasPrefix(retType, "pulumi.") {
|
|
|
|
typeAssertion = fmt.Sprintf(".(pulumi.%sOutput)", Title(retType))
|
|
|
|
}
|
2020-05-26 16:07:54 +02:00
|
|
|
|
|
|
|
if len(applyArgs) == 1 {
|
|
|
|
// If we only have a single output, just generate a normal `.Apply`
|
|
|
|
g.Fgenf(w, "%.v.ApplyT(%.v)%s", applyArgs[0], then, typeAssertion)
|
|
|
|
} else {
|
2020-06-18 02:19:39 +02:00
|
|
|
g.Fgenf(w, "pulumi.All(%.v", applyArgs[0])
|
|
|
|
applyArgs = applyArgs[1:]
|
|
|
|
for _, a := range applyArgs {
|
|
|
|
g.Fgenf(w, ",%.v", a)
|
|
|
|
}
|
2020-06-19 02:17:51 +02:00
|
|
|
allApplyThen, typeConvDecls := g.rewriteThenForAllApply(then)
|
|
|
|
g.Fgenf(w, ").ApplyT(")
|
2021-06-24 18:17:55 +02:00
|
|
|
g.genAnonymousFunctionExpression(w, allApplyThen, typeConvDecls, true)
|
2020-06-19 02:17:51 +02:00
|
|
|
g.Fgenf(w, ")%s", typeAssertion)
|
2020-05-26 16:07:54 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-27 16:32:13 +02:00
|
|
|
|
2020-06-19 02:17:51 +02:00
|
|
|
// rewriteThenForAllApply rewrites an apply func after a .All replacing params with []interface{}
|
|
|
|
// other languages like javascript take advantage of destructuring to simplify All.Apply
|
|
|
|
// by generating something like [a1, a2, a3]
|
|
|
|
// Go doesn't support this syntax so we create a set of var decls with type assertions
|
|
|
|
// to prepend to the body: a1 := _args[0].(string) ... etc.
|
|
|
|
func (g *generator) rewriteThenForAllApply(
|
|
|
|
then *model.AnonymousFunctionExpression,
|
|
|
|
) (*model.AnonymousFunctionExpression, []string) {
|
|
|
|
var typeConvDecls []string
|
|
|
|
for i, v := range then.Parameters {
|
|
|
|
typ := g.argumentTypeName(nil, v.VariableType, false)
|
|
|
|
decl := fmt.Sprintf("%s := _args[%d].(%s)", v.Name, i, typ)
|
|
|
|
typeConvDecls = append(typeConvDecls, decl)
|
|
|
|
}
|
|
|
|
|
|
|
|
// dummy type that will produce []interface{} for argumentTypeName
|
|
|
|
interfaceArrayType := &model.TupleType{
|
|
|
|
ElementTypes: []model.Type{
|
|
|
|
model.BoolType, model.StringType, model.IntType,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
then.Parameters = []*model.Variable{{
|
|
|
|
Name: "_args",
|
|
|
|
VariableType: interfaceArrayType,
|
|
|
|
}}
|
|
|
|
then.Signature.Parameters = []model.Parameter{{
|
|
|
|
Name: "_args",
|
|
|
|
Type: interfaceArrayType,
|
|
|
|
}}
|
|
|
|
|
|
|
|
return then, typeConvDecls
|
|
|
|
}
|
|
|
|
|
2020-05-27 16:32:13 +02:00
|
|
|
func (g *generator) genStringLiteral(w io.Writer, v string) {
|
2020-06-18 22:34:22 +02:00
|
|
|
g.Fgen(w, "\"")
|
|
|
|
g.Fgen(w, g.escapeString(v))
|
|
|
|
g.Fgen(w, "\"")
|
2020-05-27 16:32:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) escapeString(v string) string {
|
|
|
|
builder := strings.Builder{}
|
|
|
|
for _, c := range v {
|
|
|
|
if c == '"' || c == '\\' {
|
|
|
|
builder.WriteRune('\\')
|
|
|
|
}
|
2020-06-18 22:34:22 +02:00
|
|
|
if c == '\n' {
|
|
|
|
builder.WriteRune('\\')
|
|
|
|
builder.WriteRune('n')
|
|
|
|
continue
|
|
|
|
}
|
2020-05-27 16:32:13 +02:00
|
|
|
builder.WriteRune(c)
|
|
|
|
}
|
|
|
|
return builder.String()
|
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
|
|
|
|
// nolint: lll
|
|
|
|
func isInputty(destType model.Type) bool {
|
|
|
|
// TODO this needs to be more robust, likely the inverse of:
|
|
|
|
// https://github.com/pulumi/pulumi/blob/5330c97684cad78bcc60d8867f1b28704bd8a555/pkg/codegen/hcl2/model/type_eventuals.go#L244
|
|
|
|
switch destType := destType.(type) {
|
|
|
|
case *model.UnionType:
|
|
|
|
for _, t := range destType.ElementTypes {
|
|
|
|
if _, ok := t.(*model.OutputType); ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case *model.OutputType:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) literalKey(x model.Expression) (string, bool) {
|
|
|
|
strKey := ""
|
|
|
|
switch x := x.(type) {
|
|
|
|
case *model.LiteralValueExpression:
|
2021-06-24 18:17:55 +02:00
|
|
|
if model.StringType.AssignableFrom(x.Type()) {
|
2020-06-06 03:52:00 +02:00
|
|
|
strKey = x.Value.AsString()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
g.GenLiteralValueExpression(&buf, x)
|
|
|
|
return buf.String(), true
|
|
|
|
case *model.TemplateExpression:
|
|
|
|
if len(x.Parts) == 1 {
|
2021-06-24 18:17:55 +02:00
|
|
|
if lit, ok := x.Parts[0].(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) {
|
2020-06-06 03:52:00 +02:00
|
|
|
strKey = lit.Value.AsString()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
g.GenTemplateExpression(&buf, x)
|
|
|
|
return buf.String(), true
|
|
|
|
default:
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
return strKey, true
|
|
|
|
}
|
|
|
|
|
|
|
|
// functionName computes the go package, module, and name for the given function token.
|
2020-06-18 22:34:22 +02:00
|
|
|
func (g *generator) functionName(tokenArg model.Expression) (string, string, string, hcl.Diagnostics) {
|
2020-06-06 03:52:00 +02:00
|
|
|
token := tokenArg.(*model.TemplateExpression).Parts[0].(*model.LiteralValueExpression).Value.AsString()
|
|
|
|
tokenRange := tokenArg.SyntaxNode().Range()
|
|
|
|
|
|
|
|
// Compute the resource type from the Pulumi type token.
|
|
|
|
pkg, module, member, diagnostics := hcl2.DecomposeToken(token, tokenRange)
|
|
|
|
if strings.HasPrefix(member, "get") {
|
2020-06-18 22:34:22 +02:00
|
|
|
if g.useLookupInvokeForm(token) {
|
|
|
|
member = strings.Replace(member, "get", "lookup", 1)
|
|
|
|
}
|
2020-06-06 03:52:00 +02:00
|
|
|
}
|
2020-10-06 07:57:00 +02:00
|
|
|
modOrAlias := g.getModOrAlias(pkg, module)
|
|
|
|
mod := strings.Replace(modOrAlias, "/", ".", -1)
|
|
|
|
return pkg, mod, Title(member), diagnostics
|
2020-06-06 03:52:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var functionPackages = map[string][]string{
|
2020-06-16 08:00:02 +02:00
|
|
|
"toJSON": {"encoding/json"},
|
|
|
|
"readDir": {"io/ioutil"},
|
|
|
|
"mimeType": {"mime", "path"},
|
2020-06-06 03:52:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genFunctionPackages(x *model.FunctionCallExpression) []string {
|
|
|
|
return functionPackages[x.Name]
|
|
|
|
}
|