mirror of https://github.com/pulumi/pulumi.git
333 lines
10 KiB
Go
333 lines
10 KiB
Go
package gen
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2"
|
|
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2/model"
|
|
"github.com/pulumi/pulumi/pkg/v2/codegen/schema"
|
|
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
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.
|
|
func (g *generator) GenAnonymousFunctionExpression(w io.Writer, expr *model.AnonymousFunctionExpression) { /*TODO*/
|
|
}
|
|
|
|
// GenBinaryOpExpression generates code for a BinaryOpExpression.
|
|
func (g *generator) GenBinaryOpExpression(w io.Writer, expr *model.BinaryOpExpression) { /*TODO*/ }
|
|
|
|
// GenConditionalExpression generates code for a ConditionalExpression.
|
|
func (g *generator) GenConditionalExpression(w io.Writer, expr *model.ConditionalExpression) {}
|
|
|
|
// 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) {
|
|
switch expr.Name {
|
|
case hcl2.IntrinsicConvert:
|
|
switch arg := expr.Args[0].(type) {
|
|
case *model.TupleConsExpression:
|
|
g.genTupleConsExpression(w, arg, expr.Type())
|
|
case *model.ObjectConsExpression:
|
|
g.genObjectConsExpression(w, arg, expr.Type())
|
|
default:
|
|
g.Fgenf(w, "%.v", expr.Args[0]) // <- probably wrong w.r.t. precedence
|
|
}
|
|
case hcl2.IntrinsicApply:
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
// g.genApply(w, expr)
|
|
// case intrinsicAwait:
|
|
// g.genNYI(w, "call %v", expr.Name)
|
|
// g.Fgenf(w, "await %.17v", expr.Args[0])
|
|
// case intrinsicOutput:
|
|
// g.Fgenf(w, "Output.Create(%.v)", expr.Args[0])
|
|
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":
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
// g.Fgenf(w, "new FileAsset(%.v)", expr.Args[0])
|
|
case hcl2.Invoke:
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
// _, name := g.functionName(expr.Args[0])
|
|
|
|
// optionsBag := ""
|
|
// if len(expr.Args) == 3 {
|
|
// var buf bytes.Buffer
|
|
// g.Fgenf(&buf, ", %.v", expr.Args[2])
|
|
// optionsBag = buf.String()
|
|
// }
|
|
|
|
// g.Fgenf(w, "%s.InvokeAsync(%.v%v)", name, expr.Args[1], optionsBag)
|
|
case "length":
|
|
g.Fgenf(w, "%.20v.Length", expr.Args[0])
|
|
case "lookup":
|
|
g.genNYI(w, "Lookup")
|
|
case "range":
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
// g.genRange(w, expr, false)
|
|
case "readFile":
|
|
g.genNYI(w, "ReadFile")
|
|
case "readDir":
|
|
// TODO
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
// C# for reference
|
|
// g.Fgenf(w, "Directory.GetFiles(%.v).Select(Path.GetFileName)", expr.Args[0])
|
|
case "split":
|
|
g.Fgenf(w, "%.20v.Split(%v)", expr.Args[1], expr.Args[0])
|
|
case "toJSON":
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
// g.Fgen(w, "JsonSerializer.Serialize(")
|
|
// g.genDictionary(w, expr.Args[0])
|
|
// g.Fgen(w, ")")
|
|
default:
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
}
|
|
}
|
|
|
|
// GenIndexExpression generates code for an IndexExpression.
|
|
func (g *generator) GenIndexExpression(w io.Writer, expr *model.IndexExpression) { /*TODO*/ }
|
|
|
|
func (g *generator) GenLiteralValueExpression(w io.Writer, expr *model.LiteralValueExpression) {
|
|
switch expr.Type() {
|
|
case model.BoolType:
|
|
g.Fgenf(w, "%v", expr.Value.True())
|
|
case model.NumberType:
|
|
bf := expr.Value.AsBigFloat()
|
|
if i, acc := bf.Int64(); acc == big.Exact {
|
|
g.Fgenf(w, "%d", i)
|
|
} else {
|
|
f, _ := bf.Float64()
|
|
g.Fgenf(w, "%g", f)
|
|
}
|
|
case model.StringType:
|
|
// TODO
|
|
// g.genStringLiteral(w, expr.Value.AsString())
|
|
default:
|
|
contract.Failf("unexpected literal type in GenLiteralValueExpression: %v (%v)", expr.Type(),
|
|
expr.SyntaxNode().Range())
|
|
}
|
|
}
|
|
|
|
func (g *generator) GenObjectConsExpression(w io.Writer, expr *model.ObjectConsExpression) {
|
|
g.genObjectConsExpression(w, expr, expr.Type())
|
|
}
|
|
|
|
func (g *generator) genObjectConsExpression(w io.Writer, expr *model.ObjectConsExpression, destType model.Type) {
|
|
if len(expr.Items) > 0 {
|
|
typeName := g.argumentTypeName(expr, destType)
|
|
if typeName != "" {
|
|
g.Fgenf(w, "&%sArgs", typeName)
|
|
g.Fgenf(w, "{\n")
|
|
|
|
for _, item := range expr.Items {
|
|
lit := item.Key.(*model.LiteralValueExpression)
|
|
g.Fprint(w, Title(lit.Value.AsString()))
|
|
g.Fgenf(w, ": %.v,\n", item.Value)
|
|
}
|
|
|
|
g.Fgenf(w, "}")
|
|
} else {
|
|
// TODO
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *generator) GenRelativeTraversalExpression(w io.Writer, expr *model.RelativeTraversalExpression) {
|
|
g.Fgenf(w, "%.20v", expr.Source)
|
|
g.genRelativeTraversal(w, expr.Traversal, expr.Parts, nil)
|
|
}
|
|
|
|
func (g *generator) GenScopeTraversalExpression(w io.Writer, expr *model.ScopeTraversalExpression) {
|
|
rootName := expr.RootName
|
|
// TODO splat
|
|
// if _, ok := expr.Parts[0].(*model.SplatVariable); ok {
|
|
// rootName = "__item"
|
|
// }
|
|
|
|
g.Fgen(w, rootName)
|
|
|
|
var objType *schema.ObjectType
|
|
if resource, ok := expr.Parts[0].(*hcl2.Resource); ok {
|
|
if schemaType, ok := hcl2.GetSchemaForType(resource.InputType); ok {
|
|
objType, _ = schemaType.(*schema.ObjectType)
|
|
}
|
|
}
|
|
g.genRelativeTraversal(w, expr.Traversal.SimpleSplit().Rel, expr.Parts, objType)
|
|
}
|
|
|
|
// GenSplatExpression generates code for a SplatExpression.
|
|
func (g *generator) GenSplatExpression(w io.Writer, expr *model.SplatExpression) { /*TODO*/ }
|
|
|
|
// GenTemplateExpression generates code for a TemplateExpression.
|
|
func (g *generator) GenTemplateExpression(w io.Writer, expr *model.TemplateExpression) { /*TODO*/ }
|
|
|
|
// 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) {
|
|
argType := g.argumentTypeName(expr, destType)
|
|
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, "}")
|
|
}
|
|
|
|
// GenUnaryOpExpression generates code for a UnaryOpExpression.
|
|
func (g *generator) GenUnaryOpExpression(w io.Writer, expr *model.UnaryOpExpression) { /*TODO*/ }
|
|
|
|
// argumentTypeName computes the go type for the given expression and model type.
|
|
func (g *generator) argumentTypeName(expr model.Expression, destType model.Type) string {
|
|
if schemaType, ok := hcl2.GetSchemaForType(destType.(model.Type)); ok {
|
|
switch schemaType := schemaType.(type) {
|
|
case *schema.ArrayType:
|
|
token := schemaType.ElementType.(*schema.ObjectType).Token
|
|
tokenRange := expr.SyntaxNode().Range()
|
|
_, module, member, diags := hcl2.DecomposeToken(token, tokenRange)
|
|
importPrefix := strings.Split(module, "/")[0]
|
|
contract.Assert(len(diags) == 0)
|
|
return fmt.Sprintf("%s.%sArray", importPrefix, member)
|
|
case *schema.ObjectType:
|
|
token := schemaType.Token
|
|
tokenRange := expr.SyntaxNode().Range()
|
|
_, module, member, diags := hcl2.DecomposeToken(token, tokenRange)
|
|
importPrefix := strings.Split(module, "/")[0]
|
|
contract.Assert(len(diags) == 0)
|
|
return fmt.Sprintf("%s.%s", importPrefix, member)
|
|
default:
|
|
contract.Failf("unexpected schema type %T", schemaType)
|
|
}
|
|
}
|
|
|
|
// TODO support rest of types
|
|
return ""
|
|
}
|
|
|
|
func (g *generator) genRelativeTraversal(w io.Writer,
|
|
traversal hcl.Traversal, parts []model.Traversable, objType *schema.ObjectType) {
|
|
|
|
for _, part := range traversal {
|
|
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:
|
|
g.Fgenf(w, ".%s", Title(key.AsString()))
|
|
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
|
|
}
|
|
|
|
// lowerExpression amends the expression with intrinsics for C# generation.
|
|
func (g *generator) lowerExpression(expr model.Expression, typ model.Type) model.Expression {
|
|
expr, diags := hcl2.RewriteApplies(expr, nameInfo(0), false /*TODO*/)
|
|
contract.Assert(len(diags) == 0)
|
|
expr = hcl2.RewriteConversions(expr, typ)
|
|
|
|
return expr
|
|
}
|
|
|
|
func (g *generator) genNYI(w io.Writer, reason string, vs ...interface{}) {
|
|
g.Fgenf(w, "\"TODO: %s\"", fmt.Sprintf(reason, vs...))
|
|
}
|