These changes support arbitrary combinations of input + plain types within a schema. Handling plain types at the property level was not sufficient to support such combinations. Reifying these types required updating quite a bit of code. This is likely to have caused some temporary complications, but should eventually lead to substantial simplification in the SDK and program code generators. With the new design, input and optional types are explicit in the schema type system. Optionals will only appear at the outermost level of a type (i.e. Input<Optional<>>, Array<Optional<>>, etc. will not occur). In addition to explicit input types, each object type now has a "plain" shape and an "input" shape. The former uses only plain types; the latter uses input shapes wherever a plain type is not specified. Plain types are indicated in the schema by setting the "plain" property of a type spec to true.
2551 lines
73 KiB
Go
2551 lines
73 KiB
Go
// Copyright 2016-2020, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package model
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"strconv"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/syntax"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/convert"
|
|
)
|
|
|
|
// Expression represents a semantically-analyzed HCL2 expression.
|
|
type Expression interface {
|
|
printable
|
|
|
|
// SyntaxNode returns the hclsyntax.Node associated with the expression.
|
|
SyntaxNode() hclsyntax.Node
|
|
// NodeTokens returns the syntax.Tokens associated with the expression.
|
|
NodeTokens() syntax.NodeTokens
|
|
|
|
// SetLeadingTrivia sets the leading trivia associated with the expression.
|
|
SetLeadingTrivia(syntax.TriviaList)
|
|
// SetTrailingTrivia sets the trailing trivia associated with the expression.
|
|
SetTrailingTrivia(syntax.TriviaList)
|
|
|
|
// Type returns the type of the expression.
|
|
Type() Type
|
|
// Typecheck recomputes the type of the expression, optionally typechecking its operands first.
|
|
Typecheck(typecheckOperands bool) hcl.Diagnostics
|
|
|
|
// Evaluate evaluates the expression.
|
|
Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|
|
|
isExpression()
|
|
}
|
|
|
|
func identToken(token syntax.Token, ident string) syntax.Token {
|
|
if string(token.Raw.Bytes) != ident {
|
|
token.Raw.Bytes = []byte(ident)
|
|
}
|
|
return token
|
|
}
|
|
|
|
func exprHasLeadingTrivia(parens syntax.Parentheses, first interface{}) bool {
|
|
if parens.Any() {
|
|
return true
|
|
}
|
|
switch first := first.(type) {
|
|
case Expression:
|
|
return first.HasLeadingTrivia()
|
|
case bool:
|
|
return first
|
|
default:
|
|
contract.Failf("unexpected value of type %T for first", first)
|
|
return false
|
|
}
|
|
}
|
|
|
|
func exprHasTrailingTrivia(parens syntax.Parentheses, last interface{}) bool {
|
|
if parens.Any() {
|
|
return true
|
|
}
|
|
switch last := last.(type) {
|
|
case Expression:
|
|
return last.HasTrailingTrivia()
|
|
case bool:
|
|
return last
|
|
default:
|
|
contract.Failf("unexpected value of type %T for last", last)
|
|
return false
|
|
}
|
|
}
|
|
|
|
func getExprLeadingTrivia(parens syntax.Parentheses, first interface{}) syntax.TriviaList {
|
|
if parens.Any() {
|
|
return parens.GetLeadingTrivia()
|
|
}
|
|
switch first := first.(type) {
|
|
case Expression:
|
|
return first.GetLeadingTrivia()
|
|
case syntax.Token:
|
|
return first.LeadingTrivia
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func setExprLeadingTrivia(parens syntax.Parentheses, first interface{}, trivia syntax.TriviaList) {
|
|
if parens.Any() {
|
|
parens.SetLeadingTrivia(trivia)
|
|
return
|
|
}
|
|
switch first := first.(type) {
|
|
case Expression:
|
|
first.SetLeadingTrivia(trivia)
|
|
case *syntax.Token:
|
|
first.LeadingTrivia = trivia
|
|
}
|
|
}
|
|
|
|
func getExprTrailingTrivia(parens syntax.Parentheses, last interface{}) syntax.TriviaList {
|
|
if parens.Any() {
|
|
return parens.GetTrailingTrivia()
|
|
}
|
|
switch last := last.(type) {
|
|
case Expression:
|
|
return last.GetTrailingTrivia()
|
|
case syntax.Token:
|
|
return last.TrailingTrivia
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func setExprTrailingTrivia(parens syntax.Parentheses, last interface{}, trivia syntax.TriviaList) {
|
|
if parens.Any() {
|
|
parens.SetTrailingTrivia(trivia)
|
|
return
|
|
}
|
|
switch last := last.(type) {
|
|
case Expression:
|
|
last.SetTrailingTrivia(trivia)
|
|
case *syntax.Token:
|
|
last.TrailingTrivia = trivia
|
|
}
|
|
}
|
|
|
|
type syntaxExpr struct {
|
|
hclsyntax.LiteralValueExpr
|
|
|
|
expr Expression
|
|
}
|
|
|
|
func (x syntaxExpr) Value(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
return x.expr.Evaluate(context)
|
|
}
|
|
|
|
type hclExpression struct {
|
|
x Expression
|
|
}
|
|
|
|
func HCLExpression(x Expression) hcl.Expression {
|
|
return hclExpression{x: x}
|
|
}
|
|
|
|
func (x hclExpression) Value(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
return x.x.Evaluate(context)
|
|
}
|
|
|
|
func (x hclExpression) Variables() []hcl.Traversal {
|
|
var variables []hcl.Traversal
|
|
scope := NewRootScope(syntax.None)
|
|
_, diags := VisitExpression(x.x, func(n Expression) (Expression, hcl.Diagnostics) {
|
|
switch n := n.(type) {
|
|
case *AnonymousFunctionExpression:
|
|
scope = scope.Push(syntax.None)
|
|
for _, p := range n.Parameters {
|
|
scope.Define(p.Name, p)
|
|
}
|
|
case *ForExpression:
|
|
scope = scope.Push(syntax.None)
|
|
if n.KeyVariable != nil {
|
|
scope.Define(n.KeyVariable.Name, n.KeyVariable)
|
|
}
|
|
scope.Define(n.ValueVariable.Name, n.ValueVariable)
|
|
}
|
|
return n, nil
|
|
}, func(n Expression) (Expression, hcl.Diagnostics) {
|
|
switch n := n.(type) {
|
|
case *AnonymousFunctionExpression, *ForExpression:
|
|
scope = scope.Pop()
|
|
case *ScopeTraversalExpression:
|
|
if _, isSplatVariable := n.Parts[0].(*SplatVariable); !isSplatVariable {
|
|
if _, defined := scope.BindReference(n.RootName); !defined {
|
|
variables = append(variables, n.Traversal.SimpleSplit().Abs)
|
|
}
|
|
}
|
|
}
|
|
return n, nil
|
|
})
|
|
contract.Assert(len(diags) == 0)
|
|
return variables
|
|
}
|
|
|
|
func (x hclExpression) Range() hcl.Range {
|
|
if syntax := x.x.SyntaxNode(); syntax != nil {
|
|
return syntax.Range()
|
|
}
|
|
return hcl.Range{}
|
|
}
|
|
|
|
func (x hclExpression) StartRange() hcl.Range {
|
|
if syntax := x.x.SyntaxNode(); syntax != nil {
|
|
return syntax.(hcl.Expression).StartRange()
|
|
}
|
|
return hcl.Range{}
|
|
}
|
|
|
|
func operatorPrecedence(op *hclsyntax.Operation) int {
|
|
switch op {
|
|
case hclsyntax.OpLogicalOr:
|
|
return 1
|
|
case hclsyntax.OpLogicalAnd:
|
|
return 2
|
|
case hclsyntax.OpEqual, hclsyntax.OpNotEqual:
|
|
return 3
|
|
case hclsyntax.OpGreaterThan, hclsyntax.OpGreaterThanOrEqual, hclsyntax.OpLessThan, hclsyntax.OpLessThanOrEqual:
|
|
return 4
|
|
case hclsyntax.OpAdd, hclsyntax.OpSubtract:
|
|
return 5
|
|
case hclsyntax.OpMultiply, hclsyntax.OpDivide, hclsyntax.OpModulo:
|
|
return 6
|
|
case hclsyntax.OpNegate, hclsyntax.OpLogicalNot:
|
|
return 7
|
|
default:
|
|
return 8
|
|
}
|
|
}
|
|
|
|
// AnonymousFunctionExpression represents a semantically-analyzed anonymous function expression.
|
|
//
|
|
// These expressions are not the result of semantically analyzing syntax nodes. Instead, they may be synthesized by
|
|
// transforms over the IR for a program (e.g. the Apply transform).
|
|
type AnonymousFunctionExpression struct {
|
|
// The signature for the anonymous function.
|
|
Signature StaticFunctionSignature
|
|
// The parameter definitions for the anonymous function.
|
|
Parameters []*Variable
|
|
|
|
// The body of the anonymous function.
|
|
Body Expression
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the body of the anonymous function.
|
|
func (x *AnonymousFunctionExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Body.SyntaxNode()
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the body of the anonymous function.
|
|
func (x *AnonymousFunctionExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Body.NodeTokens()
|
|
}
|
|
|
|
// Type returns the type of the anonymous function expression.
|
|
//
|
|
// TODO: currently this returns the any type. Instead, it should return a function type.
|
|
func (x *AnonymousFunctionExpression) Type() Type {
|
|
return DynamicType
|
|
}
|
|
|
|
func (x *AnonymousFunctionExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
if typecheckOperands {
|
|
bodyDiags := x.Body.Typecheck(true)
|
|
diagnostics = append(diagnostics, bodyDiags...)
|
|
}
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *AnonymousFunctionExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
return cty.NilVal, hcl.Diagnostics{cannotEvaluateAnonymousFunctionExpressions()}
|
|
}
|
|
|
|
func (x *AnonymousFunctionExpression) HasLeadingTrivia() bool {
|
|
return x.Body.HasLeadingTrivia()
|
|
}
|
|
|
|
func (x *AnonymousFunctionExpression) HasTrailingTrivia() bool {
|
|
return x.Body.HasTrailingTrivia()
|
|
}
|
|
|
|
func (x *AnonymousFunctionExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return x.Body.GetLeadingTrivia()
|
|
}
|
|
|
|
func (x *AnonymousFunctionExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
x.Body.SetLeadingTrivia(t)
|
|
}
|
|
|
|
func (x *AnonymousFunctionExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
return x.Body.GetTrailingTrivia()
|
|
}
|
|
|
|
func (x *AnonymousFunctionExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
x.Body.SetTrailingTrivia(t)
|
|
}
|
|
|
|
func (x *AnonymousFunctionExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *AnonymousFunctionExpression) print(w io.Writer, p *printer) {
|
|
// Print a call to eval.
|
|
p.fprintf(w, "eval(")
|
|
|
|
// Print the parameter names.
|
|
for _, v := range x.Parameters {
|
|
p.fprintf(w, "%v, ", v.Name)
|
|
}
|
|
|
|
// Print the body and closing paren.
|
|
p.fprintf(w, "%v)", x.Body)
|
|
}
|
|
|
|
func (*AnonymousFunctionExpression) isExpression() {}
|
|
|
|
// BinaryOpExpression represents a semantically-analyzed binary operation.
|
|
type BinaryOpExpression struct {
|
|
// The syntax node associated with the binary operation.
|
|
Syntax *hclsyntax.BinaryOpExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.BinaryOpTokens
|
|
|
|
// The left-hand operand of the operation.
|
|
LeftOperand Expression
|
|
// The operation.
|
|
Operation *hclsyntax.Operation
|
|
// The right-hand operand of the operation.
|
|
RightOperand Expression
|
|
|
|
leftType Type
|
|
rightType Type
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the binary operation.
|
|
func (x *BinaryOpExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the binary operation.
|
|
func (x *BinaryOpExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// LeftOperandType returns the desired type for the left operand of the binary operation.
|
|
func (x *BinaryOpExpression) LeftOperandType() Type {
|
|
return x.leftType
|
|
}
|
|
|
|
// RightOperandType returns the desired type for the right operand of the binary operation.
|
|
func (x *BinaryOpExpression) RightOperandType() Type {
|
|
return x.rightType
|
|
}
|
|
|
|
// Type returns the type of the binary operation.
|
|
func (x *BinaryOpExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *BinaryOpExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
var rng hcl.Range
|
|
if x.Syntax != nil {
|
|
rng = x.Syntax.Range()
|
|
}
|
|
|
|
if typecheckOperands {
|
|
leftDiags := x.LeftOperand.Typecheck(true)
|
|
diagnostics = append(diagnostics, leftDiags...)
|
|
|
|
rightDiags := x.RightOperand.Typecheck(true)
|
|
diagnostics = append(diagnostics, rightDiags...)
|
|
}
|
|
|
|
// Compute the signature for the operator and typecheck the arguments.
|
|
signature := getOperationSignature(x.Operation)
|
|
contract.Assert(len(signature.Parameters) == 2)
|
|
|
|
x.leftType = signature.Parameters[0].Type
|
|
x.rightType = signature.Parameters[1].Type
|
|
|
|
typecheckDiags := typecheckArgs(rng, signature, x.LeftOperand, x.RightOperand)
|
|
diagnostics = append(diagnostics, typecheckDiags...)
|
|
|
|
x.exprType = liftOperationType(signature.ReturnType, x.LeftOperand, x.RightOperand)
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *BinaryOpExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.BinaryOpExpr{
|
|
LHS: &syntaxExpr{expr: x.LeftOperand},
|
|
Op: x.Operation,
|
|
RHS: &syntaxExpr{expr: x.RightOperand},
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *BinaryOpExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.LeftOperand)
|
|
}
|
|
|
|
func (x *BinaryOpExpression) HasTrailingTrivia() bool {
|
|
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.RightOperand)
|
|
}
|
|
|
|
func (x *BinaryOpExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.LeftOperand)
|
|
}
|
|
|
|
func (x *BinaryOpExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
setExprLeadingTrivia(x.Tokens.GetParentheses(), x.LeftOperand, t)
|
|
}
|
|
|
|
func (x *BinaryOpExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.RightOperand)
|
|
}
|
|
|
|
func (x *BinaryOpExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewBinaryOpTokens(x.Operation)
|
|
}
|
|
setExprTrailingTrivia(x.Tokens.GetParentheses(), x.RightOperand, t)
|
|
}
|
|
|
|
func (x *BinaryOpExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *BinaryOpExpression) print(w io.Writer, p *printer) {
|
|
precedence := operatorPrecedence(x.Operation)
|
|
p.fprintf(w, "%[2](%.[1]*[3]v% [4]v% .[1]*[5]o%[6])",
|
|
precedence,
|
|
x.Tokens.GetParentheses(),
|
|
x.LeftOperand, x.Tokens.GetOperator(x.Operation), x.RightOperand,
|
|
x.Tokens.GetParentheses())
|
|
}
|
|
|
|
func (*BinaryOpExpression) isExpression() {}
|
|
|
|
// ConditionalExpression represents a semantically-analzed conditional expression (i.e.
|
|
// <condition> '?' <true> ':' <false>).
|
|
type ConditionalExpression struct {
|
|
// The syntax node associated with the conditional expression.
|
|
Syntax *hclsyntax.ConditionalExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens syntax.NodeTokens
|
|
|
|
// The condition.
|
|
Condition Expression
|
|
// The result of the expression if the condition evaluates to true.
|
|
TrueResult Expression
|
|
// The result of the expression if the condition evaluates to false.
|
|
FalseResult Expression
|
|
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the conditional expression.
|
|
func (x *ConditionalExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the conditional expression.
|
|
func (x *ConditionalExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the conditional expression.
|
|
func (x *ConditionalExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *ConditionalExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
if typecheckOperands {
|
|
conditionDiags := x.Condition.Typecheck(true)
|
|
diagnostics = append(diagnostics, conditionDiags...)
|
|
|
|
trueDiags := x.TrueResult.Typecheck(true)
|
|
diagnostics = append(diagnostics, trueDiags...)
|
|
|
|
falseDiags := x.FalseResult.Typecheck(true)
|
|
diagnostics = append(diagnostics, falseDiags...)
|
|
}
|
|
|
|
// Compute the type of the result.
|
|
resultType, _ := UnifyTypes(x.TrueResult.Type(), x.FalseResult.Type())
|
|
|
|
// Typecheck the condition expression.
|
|
if InputType(BoolType).ConversionFrom(x.Condition.Type()) == NoConversion {
|
|
diagnostics = append(diagnostics, ExprNotConvertible(InputType(BoolType), x.Condition))
|
|
}
|
|
|
|
x.exprType = liftOperationType(resultType, x.Condition)
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *ConditionalExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.ConditionalExpr{
|
|
Condition: &syntaxExpr{expr: x.Condition},
|
|
TrueResult: &syntaxExpr{expr: x.TrueResult},
|
|
FalseResult: &syntaxExpr{expr: x.FalseResult},
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *ConditionalExpression) HasLeadingTrivia() bool {
|
|
switch tokens := x.Tokens.(type) {
|
|
case *syntax.ConditionalTokens:
|
|
if tokens.Parentheses.Any() {
|
|
return true
|
|
}
|
|
case *syntax.TemplateConditionalTokens:
|
|
return len(tokens.OpenIf.LeadingTrivia) != 0
|
|
}
|
|
return x.Condition.HasLeadingTrivia()
|
|
}
|
|
|
|
func (x *ConditionalExpression) HasTrailingTrivia() bool {
|
|
switch tokens := x.Tokens.(type) {
|
|
case *syntax.ConditionalTokens:
|
|
if tokens.Parentheses.Any() {
|
|
return true
|
|
}
|
|
case *syntax.TemplateConditionalTokens:
|
|
return len(tokens.CloseEndif.TrailingTrivia) != 0
|
|
}
|
|
return x.FalseResult.HasTrailingTrivia()
|
|
}
|
|
|
|
func (x *ConditionalExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
switch tokens := x.Tokens.(type) {
|
|
case *syntax.ConditionalTokens:
|
|
if tokens.Parentheses.Any() {
|
|
return tokens.Parentheses.GetLeadingTrivia()
|
|
}
|
|
case *syntax.TemplateConditionalTokens:
|
|
return tokens.OpenIf.LeadingTrivia
|
|
}
|
|
return x.Condition.GetLeadingTrivia()
|
|
}
|
|
|
|
func (x *ConditionalExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
switch tokens := x.Tokens.(type) {
|
|
case *syntax.ConditionalTokens:
|
|
if tokens.Parentheses.Any() {
|
|
tokens.Parentheses.SetLeadingTrivia(t)
|
|
return
|
|
}
|
|
case *syntax.TemplateConditionalTokens:
|
|
tokens.OpenIf.LeadingTrivia = t
|
|
return
|
|
}
|
|
x.Condition.SetLeadingTrivia(t)
|
|
}
|
|
|
|
func (x *ConditionalExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
switch tokens := x.Tokens.(type) {
|
|
case *syntax.ConditionalTokens:
|
|
if tokens.Parentheses.Any() {
|
|
return tokens.Parentheses.GetTrailingTrivia()
|
|
}
|
|
case *syntax.TemplateConditionalTokens:
|
|
return tokens.CloseEndif.TrailingTrivia
|
|
}
|
|
return x.FalseResult.GetTrailingTrivia()
|
|
}
|
|
|
|
func (x *ConditionalExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewConditionalTokens()
|
|
}
|
|
switch tokens := x.Tokens.(type) {
|
|
case *syntax.ConditionalTokens:
|
|
if tokens.Parentheses.Any() {
|
|
tokens.Parentheses.SetTrailingTrivia(t)
|
|
return
|
|
}
|
|
case *syntax.TemplateConditionalTokens:
|
|
tokens.CloseEndif.TrailingTrivia = t
|
|
return
|
|
}
|
|
x.FalseResult.SetTrailingTrivia(t)
|
|
}
|
|
|
|
func (x *ConditionalExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *ConditionalExpression) print(w io.Writer, p *printer) {
|
|
tokens := x.Tokens
|
|
if tokens == nil {
|
|
tokens = syntax.NewConditionalTokens()
|
|
}
|
|
|
|
switch tokens := tokens.(type) {
|
|
case *syntax.ConditionalTokens:
|
|
p.fprintf(w, "%(%v% v% v% v% v%)",
|
|
tokens.Parentheses,
|
|
x.Condition, tokens.QuestionMark, x.TrueResult, tokens.Colon, x.FalseResult,
|
|
tokens.Parentheses)
|
|
case *syntax.TemplateConditionalTokens:
|
|
p.fprintf(w, "%v%v% v%v%v", tokens.OpenIf, tokens.If, x.Condition, tokens.CloseIf, x.TrueResult)
|
|
if tokens.Else != nil {
|
|
p.fprintf(w, "%v%v%v%v", tokens.OpenElse, tokens.Else, tokens.CloseElse, x.FalseResult)
|
|
}
|
|
p.fprintf(w, "%v%v%v", tokens.OpenEndif, tokens.Endif, tokens.CloseEndif)
|
|
}
|
|
}
|
|
|
|
func (*ConditionalExpression) isExpression() {}
|
|
|
|
// ErrorExpression represents an expression that could not be bound due to an error.
|
|
type ErrorExpression struct {
|
|
// The syntax node associated with the error, if any.
|
|
Syntax hclsyntax.Node
|
|
// The tokens associated with the error.
|
|
Tokens syntax.NodeTokens
|
|
// The message associated with the error.
|
|
Message string
|
|
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the error expression.
|
|
func (x *ErrorExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the error expression.
|
|
func (x *ErrorExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the error expression.
|
|
func (x *ErrorExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *ErrorExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
return nil
|
|
}
|
|
|
|
func (x *ErrorExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
return cty.DynamicVal, hcl.Diagnostics{{
|
|
Severity: hcl.DiagError,
|
|
Summary: x.Message,
|
|
}}
|
|
}
|
|
|
|
func (x *ErrorExpression) HasLeadingTrivia() bool {
|
|
return false
|
|
}
|
|
|
|
func (x *ErrorExpression) HasTrailingTrivia() bool {
|
|
return false
|
|
}
|
|
|
|
func (x *ErrorExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return nil
|
|
}
|
|
|
|
func (x *ErrorExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
}
|
|
|
|
func (x *ErrorExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
return nil
|
|
}
|
|
|
|
func (x *ErrorExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
}
|
|
|
|
func (x *ErrorExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *ErrorExpression) print(w io.Writer, p *printer) {
|
|
p.fprintf(w, "error(%q)", x.Message)
|
|
}
|
|
|
|
func (*ErrorExpression) isExpression() {}
|
|
|
|
// ForExpression represents a semantically-analyzed for expression.
|
|
type ForExpression struct {
|
|
fmt.Formatter
|
|
|
|
// The syntax node associated with the for expression.
|
|
Syntax *hclsyntax.ForExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens syntax.NodeTokens
|
|
|
|
// The key variable, if any.
|
|
KeyVariable *Variable
|
|
// The value variable.
|
|
ValueVariable *Variable
|
|
|
|
// The collection being iterated.
|
|
Collection Expression
|
|
// The expression that generates the keys of the result, if any. If this field is non-nil, the result is a map.
|
|
Key Expression
|
|
// The expression that generates the values of the result.
|
|
Value Expression
|
|
// The condition that filters the items of the result, if any.
|
|
Condition Expression
|
|
|
|
// True if the value expression is being grouped.
|
|
Group bool
|
|
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the for expression.
|
|
func (x *ForExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the for expression.
|
|
func (x *ForExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the for expression.
|
|
func (x *ForExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *ForExpression) typecheck(typecheckCollection, typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
var rng hcl.Range
|
|
if x.Syntax != nil {
|
|
rng = x.Syntax.CollExpr.Range()
|
|
}
|
|
|
|
if typecheckOperands {
|
|
collectionDiags := x.Collection.Typecheck(true)
|
|
diagnostics = append(diagnostics, collectionDiags...)
|
|
}
|
|
|
|
if typecheckCollection {
|
|
// Poke through any eventual and optional types that may wrap the collection type.
|
|
collectionType := unwrapIterableSourceType(x.Collection.Type())
|
|
|
|
keyType, valueType, kvDiags := GetCollectionTypes(collectionType, rng)
|
|
diagnostics = append(diagnostics, kvDiags...)
|
|
|
|
if x.KeyVariable != nil {
|
|
x.KeyVariable.VariableType = keyType
|
|
}
|
|
x.ValueVariable.VariableType = valueType
|
|
}
|
|
|
|
if typecheckOperands {
|
|
if x.Key != nil {
|
|
keyDiags := x.Key.Typecheck(true)
|
|
diagnostics = append(diagnostics, keyDiags...)
|
|
}
|
|
|
|
valueDiags := x.Value.Typecheck(true)
|
|
diagnostics = append(diagnostics, valueDiags...)
|
|
|
|
if x.Condition != nil {
|
|
conditionDiags := x.Condition.Typecheck(true)
|
|
diagnostics = append(diagnostics, conditionDiags...)
|
|
}
|
|
}
|
|
|
|
if x.Key != nil {
|
|
// A key expression is only present when producing a map. Key types must therefore be strings.
|
|
if !InputType(StringType).ConversionFrom(x.Key.Type()).Exists() {
|
|
diagnostics = append(diagnostics, ExprNotConvertible(InputType(StringType), x.Key))
|
|
}
|
|
}
|
|
|
|
if x.Condition != nil {
|
|
if !InputType(BoolType).ConversionFrom(x.Condition.Type()).Exists() {
|
|
diagnostics = append(diagnostics, ExprNotConvertible(InputType(BoolType), x.Condition))
|
|
}
|
|
}
|
|
|
|
// If there is a key expression, we are producing a map. Otherwise, we are producing an list. In either case, wrap
|
|
// the result type in the same set of eventuals and optionals present in the collection type.
|
|
var resultType Type
|
|
if x.Key != nil {
|
|
valueType := x.Value.Type()
|
|
if x.Group {
|
|
valueType = NewListType(valueType)
|
|
}
|
|
resultType = wrapIterableResultType(x.Collection.Type(), NewMapType(valueType))
|
|
} else {
|
|
resultType = wrapIterableResultType(x.Collection.Type(), NewListType(x.Value.Type()))
|
|
}
|
|
|
|
// If either the key expression or the condition expression is eventual, the result is eventual: each of these
|
|
// values is required to determine which items are present in the result.
|
|
var liftArgs []Expression
|
|
if x.Key != nil {
|
|
liftArgs = append(liftArgs, x.Key)
|
|
}
|
|
if x.Condition != nil {
|
|
liftArgs = append(liftArgs, x.Condition)
|
|
}
|
|
|
|
x.exprType = liftOperationType(resultType, liftArgs...)
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *ForExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
return x.typecheck(true, typecheckOperands)
|
|
}
|
|
|
|
func (x *ForExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.ForExpr{
|
|
ValVar: x.ValueVariable.Name,
|
|
CollExpr: &syntaxExpr{expr: x.Collection},
|
|
ValExpr: &syntaxExpr{expr: x.Value},
|
|
Group: x.Group,
|
|
}
|
|
if x.KeyVariable != nil {
|
|
syntax.KeyVar = x.KeyVariable.Name
|
|
}
|
|
if x.Key != nil {
|
|
syntax.KeyExpr = &syntaxExpr{expr: x.Key}
|
|
}
|
|
if x.Condition != nil {
|
|
syntax.CondExpr = &syntaxExpr{expr: x.Condition}
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *ForExpression) HasLeadingTrivia() bool {
|
|
return x.Tokens != nil
|
|
}
|
|
|
|
func (x *ForExpression) HasTrailingTrivia() bool {
|
|
return x.Tokens != nil
|
|
}
|
|
|
|
func (x *ForExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
switch tokens := x.Tokens.(type) {
|
|
case *syntax.ForTokens:
|
|
return getExprLeadingTrivia(tokens.Parentheses, tokens.Open)
|
|
case *syntax.TemplateForTokens:
|
|
return tokens.OpenFor.LeadingTrivia
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (x *ForExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
keyVariable := ""
|
|
if x.KeyVariable != nil {
|
|
keyVariable = x.KeyVariable.Name
|
|
}
|
|
x.Tokens = syntax.NewForTokens(keyVariable, x.ValueVariable.Name, x.Key != nil, x.Group, x.Condition != nil)
|
|
}
|
|
switch tokens := x.Tokens.(type) {
|
|
case *syntax.ForTokens:
|
|
setExprLeadingTrivia(tokens.Parentheses, &tokens.Open, t)
|
|
case *syntax.TemplateForTokens:
|
|
tokens.OpenFor.LeadingTrivia = t
|
|
}
|
|
}
|
|
|
|
func (x *ForExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
switch tokens := x.Tokens.(type) {
|
|
case *syntax.ForTokens:
|
|
return getExprTrailingTrivia(tokens.Parentheses, tokens.Close)
|
|
case *syntax.TemplateForTokens:
|
|
return tokens.CloseEndfor.TrailingTrivia
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (x *ForExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
keyVariable := ""
|
|
if x.KeyVariable != nil {
|
|
keyVariable = x.KeyVariable.Name
|
|
}
|
|
x.Tokens = syntax.NewForTokens(keyVariable, x.ValueVariable.Name, x.Key != nil, x.Group, x.Condition != nil)
|
|
}
|
|
switch tokens := x.Tokens.(type) {
|
|
case *syntax.ForTokens:
|
|
setExprTrailingTrivia(tokens.Parentheses, &tokens.Close, t)
|
|
case *syntax.TemplateForTokens:
|
|
tokens.CloseEndfor.TrailingTrivia = t
|
|
}
|
|
}
|
|
|
|
func (x *ForExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *ForExpression) print(w io.Writer, p *printer) {
|
|
tokens := x.Tokens
|
|
if tokens == nil {
|
|
keyVariable := ""
|
|
if x.KeyVariable != nil {
|
|
keyVariable = x.KeyVariable.Name
|
|
}
|
|
syntax.NewForTokens(keyVariable, x.ValueVariable.Name, x.Key != nil, x.Group, x.Condition != nil)
|
|
}
|
|
|
|
switch tokens := tokens.(type) {
|
|
case *syntax.ForTokens:
|
|
// Print the opening rune and the for token.
|
|
p.fprintf(w, "%(%v%v", tokens.Parentheses, tokens.Open, tokens.For)
|
|
|
|
// Print the key variable, if any.
|
|
if x.KeyVariable != nil {
|
|
keyToken := tokens.Key
|
|
if x.KeyVariable != nil && keyToken == nil {
|
|
keyToken = &syntax.Token{Raw: hclsyntax.Token{Type: hclsyntax.TokenIdent}}
|
|
}
|
|
|
|
key := identToken(*keyToken, x.KeyVariable.Name)
|
|
p.fprintf(w, "% v%v", key, tokens.Comma)
|
|
}
|
|
|
|
// Print the value variable, the in token, the collection expression, and the colon.
|
|
value := identToken(tokens.Value, x.ValueVariable.Name)
|
|
p.fprintf(w, "% v% v% v%v", value, tokens.In, x.Collection, tokens.Colon)
|
|
|
|
// Print the key expression and arrow token, if any.
|
|
if x.Key != nil {
|
|
p.fprintf(w, "% v% v", x.Key, tokens.Arrow)
|
|
}
|
|
|
|
// Print the value expression.
|
|
p.fprintf(w, "% v", x.Value)
|
|
|
|
// Print the group token, if any.
|
|
if x.Group {
|
|
p.fprintf(w, "%v", tokens.Group)
|
|
}
|
|
|
|
// Print the if token and the condition, if any.
|
|
if x.Condition != nil {
|
|
p.fprintf(w, "% v% v", tokens.If, x.Condition)
|
|
}
|
|
|
|
// Print the closing rune.
|
|
p.fprintf(w, "%v%)", tokens.Close, tokens.Parentheses)
|
|
case *syntax.TemplateForTokens:
|
|
// Print the opening sequence.
|
|
p.fprintf(w, "%v%v", tokens.OpenFor, tokens.For)
|
|
|
|
// Print the key variable, if any.
|
|
if x.KeyVariable != nil {
|
|
keyToken := tokens.Key
|
|
if x.KeyVariable != nil && keyToken == nil {
|
|
keyToken = &syntax.Token{Raw: hclsyntax.Token{Type: hclsyntax.TokenIdent}}
|
|
}
|
|
|
|
key := identToken(*keyToken, x.KeyVariable.Name)
|
|
p.fprintf(w, "% v%v", key, tokens.Comma)
|
|
}
|
|
|
|
// Print the value variable, the in token, the collection expression, the control sequence terminator, the
|
|
// value expression, and the closing sequence.
|
|
p.fprintf(w, "% v% v% v%v%v%v%v%v",
|
|
identToken(tokens.Value, x.ValueVariable.Name), tokens.In, x.Collection, tokens.CloseFor,
|
|
x.Value,
|
|
tokens.OpenEndfor, tokens.Endfor, tokens.CloseEndfor)
|
|
}
|
|
}
|
|
|
|
func (*ForExpression) isExpression() {}
|
|
|
|
// FunctionCallExpression represents a semantically-analyzed function call expression.
|
|
type FunctionCallExpression struct {
|
|
// The syntax node associated with the function call expression.
|
|
Syntax *hclsyntax.FunctionCallExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.FunctionCallTokens
|
|
|
|
// The name of the called function.
|
|
Name string
|
|
// The signature of the called function.
|
|
Signature StaticFunctionSignature
|
|
// The arguments to the function call.
|
|
Args []Expression
|
|
// ExpandFinal indicates that the final argument should be a tuple, list, or set whose elements will be passed as
|
|
// individual arguments to the function.
|
|
ExpandFinal bool
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the function call expression.
|
|
func (x *FunctionCallExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the function call expression.
|
|
func (x *FunctionCallExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the function call expression.
|
|
func (x *FunctionCallExpression) Type() Type {
|
|
return x.Signature.ReturnType
|
|
}
|
|
|
|
func (x *FunctionCallExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
if typecheckOperands {
|
|
for _, arg := range x.Args {
|
|
argDiagnostics := arg.Typecheck(true)
|
|
diagnostics = append(diagnostics, argDiagnostics...)
|
|
}
|
|
}
|
|
|
|
var rng hcl.Range
|
|
if x.Syntax != nil {
|
|
rng = x.Syntax.Range()
|
|
}
|
|
|
|
// Typecheck the function's arguments.
|
|
typecheckDiags := typecheckArgs(rng, x.Signature, x.Args...)
|
|
diagnostics = append(diagnostics, typecheckDiags...)
|
|
|
|
x.Signature.ReturnType = liftOperationType(x.Signature.ReturnType, x.Args...)
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *FunctionCallExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.FunctionCallExpr{
|
|
Name: x.Name,
|
|
Args: make([]hclsyntax.Expression, len(x.Args)),
|
|
ExpandFinal: x.ExpandFinal,
|
|
}
|
|
for i, arg := range x.Args {
|
|
syntax.Args[i] = &syntaxExpr{expr: arg}
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *FunctionCallExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *FunctionCallExpression) HasTrailingTrivia() bool {
|
|
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *FunctionCallExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetName(x.Name))
|
|
}
|
|
|
|
func (x *FunctionCallExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewFunctionCallTokens(x.Name, len(x.Args))
|
|
}
|
|
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.Name, t)
|
|
}
|
|
|
|
func (x *FunctionCallExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetCloseParen())
|
|
}
|
|
|
|
func (x *FunctionCallExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewFunctionCallTokens(x.Name, len(x.Args))
|
|
}
|
|
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.CloseParen, t)
|
|
}
|
|
|
|
func (x *FunctionCallExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *FunctionCallExpression) print(w io.Writer, p *printer) {
|
|
// Print the name and opening parenthesis.
|
|
p.fprintf(w, "%(%v%v", x.Tokens.GetParentheses(), x.Tokens.GetName(x.Name), x.Tokens.GetOpenParen())
|
|
|
|
// Print each argument and its comma.
|
|
commas := x.Tokens.GetCommas(len(x.Args))
|
|
for i, arg := range x.Args {
|
|
if i == 0 {
|
|
p.fprintf(w, "%v", arg)
|
|
} else {
|
|
p.fprintf(w, "% v", arg)
|
|
}
|
|
|
|
if i < len(x.Args)-1 {
|
|
var comma syntax.Token
|
|
if i < len(commas) {
|
|
comma = commas[i]
|
|
}
|
|
p.fprintf(w, "%v", comma)
|
|
}
|
|
}
|
|
|
|
// If there were commas left over, print the trivia for each.
|
|
if len(x.Args) > 0 && len(x.Args)-1 <= len(commas) {
|
|
for _, comma := range commas[len(x.Args)-1:] {
|
|
p.fprintf(w, "%v", comma.AllTrivia().CollapseWhitespace())
|
|
}
|
|
}
|
|
|
|
// Print the closing parenthesis.
|
|
p.fprintf(w, "%v%)", x.Tokens.GetCloseParen(), x.Tokens.GetParentheses())
|
|
}
|
|
|
|
func (*FunctionCallExpression) isExpression() {}
|
|
|
|
// IndexExpression represents a semantically-analyzed index expression.
|
|
type IndexExpression struct {
|
|
// The syntax node associated with the index expression.
|
|
Syntax *hclsyntax.IndexExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.IndexTokens
|
|
|
|
// The collection being indexed.
|
|
Collection Expression
|
|
// The index key.
|
|
Key Expression
|
|
|
|
keyType Type
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the index expression.
|
|
func (x *IndexExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the index expression.
|
|
func (x *IndexExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// KeyType returns the expected type of the index expression's key.
|
|
func (x *IndexExpression) KeyType() Type {
|
|
return x.keyType
|
|
}
|
|
|
|
// Type returns the type of the index expression.
|
|
func (x *IndexExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *IndexExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
if typecheckOperands {
|
|
collectionDiags := x.Collection.Typecheck(true)
|
|
diagnostics = append(diagnostics, collectionDiags...)
|
|
|
|
keyDiags := x.Key.Typecheck(true)
|
|
diagnostics = append(diagnostics, keyDiags...)
|
|
}
|
|
|
|
var rng hcl.Range
|
|
if x.Syntax != nil {
|
|
rng = x.Syntax.Collection.Range()
|
|
}
|
|
|
|
collectionType := unwrapIterableSourceType(x.Collection.Type())
|
|
keyType, valueType, kvDiags := GetCollectionTypes(collectionType, rng)
|
|
diagnostics = append(diagnostics, kvDiags...)
|
|
x.keyType = keyType
|
|
|
|
if lit, ok := x.Key.(*LiteralValueExpression); ok {
|
|
traverser := hcl.TraverseIndex{
|
|
Key: lit.Value,
|
|
}
|
|
valueType, traverseDiags := x.Collection.Type().Traverse(traverser)
|
|
if len(traverseDiags) == 0 {
|
|
x.exprType = valueType.(Type)
|
|
return diagnostics
|
|
}
|
|
}
|
|
|
|
if !InputType(keyType).ConversionFrom(x.Key.Type()).Exists() {
|
|
diagnostics = append(diagnostics, ExprNotConvertible(InputType(keyType), x.Key))
|
|
}
|
|
|
|
resultType := wrapIterableResultType(x.Collection.Type(), valueType)
|
|
x.exprType = liftOperationType(resultType, x.Key)
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *IndexExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.IndexExpr{
|
|
Collection: &syntaxExpr{expr: x.Collection},
|
|
Key: &syntaxExpr{expr: x.Key},
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *IndexExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Collection)
|
|
}
|
|
|
|
func (x *IndexExpression) HasTrailingTrivia() bool {
|
|
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *IndexExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetOpenBracket())
|
|
}
|
|
|
|
func (x *IndexExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewIndexTokens()
|
|
}
|
|
setExprLeadingTrivia(x.Tokens.Parentheses, x.Collection, t)
|
|
}
|
|
|
|
func (x *IndexExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetCloseBracket())
|
|
}
|
|
|
|
func (x *IndexExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewIndexTokens()
|
|
}
|
|
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.CloseBracket, t)
|
|
}
|
|
|
|
func (x *IndexExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *IndexExpression) print(w io.Writer, p *printer) {
|
|
p.fprintf(w, "%(%v%v%v%v%)",
|
|
x.Tokens.GetParentheses(),
|
|
x.Collection, x.Tokens.GetOpenBracket(), x.Key, x.Tokens.GetCloseBracket(),
|
|
x.Tokens.GetParentheses())
|
|
}
|
|
|
|
func (*IndexExpression) isExpression() {}
|
|
|
|
func literalText(value cty.Value, rawBytes []byte, escaped, quoted bool) string {
|
|
if len(rawBytes) > 0 {
|
|
parsed, diags := hclsyntax.ParseExpression(rawBytes, "", hcl.Pos{})
|
|
if !diags.HasErrors() {
|
|
if lit, ok := parsed.(*hclsyntax.LiteralValueExpr); ok && lit.Val.RawEquals(value) {
|
|
return string(rawBytes)
|
|
}
|
|
}
|
|
}
|
|
|
|
switch value.Type() {
|
|
case cty.Bool:
|
|
if value.True() {
|
|
return "true"
|
|
}
|
|
return "false"
|
|
case cty.Number:
|
|
bf := value.AsBigFloat()
|
|
i, acc := bf.Int64()
|
|
if acc == big.Exact {
|
|
return fmt.Sprintf("%v", i)
|
|
}
|
|
d, _ := bf.Float64()
|
|
return fmt.Sprintf("%g", d)
|
|
case cty.String:
|
|
if !escaped {
|
|
return value.AsString()
|
|
}
|
|
s := strconv.Quote(value.AsString())
|
|
if !quoted {
|
|
return s[1 : len(s)-1]
|
|
}
|
|
return s
|
|
default:
|
|
panic(fmt.Errorf("unexpected literal type %v", value.Type().FriendlyName()))
|
|
}
|
|
}
|
|
|
|
// LiteralValueExpression represents a semantically-analyzed literal value expression.
|
|
type LiteralValueExpression struct {
|
|
// The syntax node associated with the literal value expression.
|
|
Syntax *hclsyntax.LiteralValueExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.LiteralValueTokens
|
|
|
|
// The value of the expression.
|
|
Value cty.Value
|
|
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the literal value expression.
|
|
func (x *LiteralValueExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the literal value expression.
|
|
func (x *LiteralValueExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the literal value expression.
|
|
func (x *LiteralValueExpression) Type() Type {
|
|
if x.exprType == nil {
|
|
typ := ctyTypeToType(x.Value.Type(), false)
|
|
x.exprType = NewConstType(typ, x.Value)
|
|
}
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *LiteralValueExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
typ := NoneType
|
|
if !x.Value.IsNull() {
|
|
typ = ctyTypeToType(x.Value.Type(), false)
|
|
}
|
|
|
|
switch {
|
|
case typ == NoneType || typ == StringType || typ == IntType || typ == NumberType || typ == BoolType:
|
|
// OK
|
|
typ = NewConstType(typ, x.Value)
|
|
default:
|
|
var rng hcl.Range
|
|
if x.Syntax != nil {
|
|
rng = x.Syntax.Range()
|
|
}
|
|
typ, diagnostics = DynamicType, hcl.Diagnostics{unsupportedLiteralValue(x.Value, rng)}
|
|
}
|
|
|
|
x.exprType = typ
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *LiteralValueExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.LiteralValueExpr{
|
|
Val: x.Value,
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *LiteralValueExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *LiteralValueExpression) HasTrailingTrivia() bool {
|
|
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *LiteralValueExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
if v := x.Tokens.GetValue(x.Value); len(v) > 0 {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), v[0])
|
|
}
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), nil)
|
|
}
|
|
|
|
func (x *LiteralValueExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewLiteralValueTokens(x.Value)
|
|
}
|
|
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.Value[0], t)
|
|
}
|
|
|
|
func (x *LiteralValueExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
if v := x.Tokens.GetValue(x.Value); len(v) > 0 {
|
|
return getExprTrailingTrivia(x.Tokens.GetParentheses(), v[len(v)-1])
|
|
}
|
|
return getExprTrailingTrivia(x.Tokens.GetParentheses(), nil)
|
|
}
|
|
|
|
func (x *LiteralValueExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewLiteralValueTokens(x.Value)
|
|
}
|
|
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.Value[len(x.Tokens.Value)-1], t)
|
|
}
|
|
|
|
func (x *LiteralValueExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *LiteralValueExpression) printLit(w io.Writer, p *printer, escaped bool) {
|
|
// Literals are... odd. They may be composed of multiple tokens, but those tokens should never contain interior
|
|
// trivia.
|
|
|
|
var leading, trailing syntax.TriviaList
|
|
var rawBytes []byte
|
|
if toks := x.Tokens.GetValue(x.Value); len(toks) > 0 {
|
|
leading, trailing = toks[0].LeadingTrivia, toks[len(toks)-1].TrailingTrivia
|
|
|
|
for _, t := range toks {
|
|
rawBytes = append(rawBytes, t.Raw.Bytes...)
|
|
}
|
|
}
|
|
|
|
p.fprintf(w, "%(%v%v%v%)",
|
|
x.Tokens.GetParentheses(),
|
|
leading, literalText(x.Value, rawBytes, escaped, false), trailing,
|
|
x.Tokens.GetParentheses())
|
|
}
|
|
|
|
func (x *LiteralValueExpression) print(w io.Writer, p *printer) {
|
|
x.printLit(w, p, false)
|
|
}
|
|
|
|
func (*LiteralValueExpression) isExpression() {}
|
|
|
|
// ObjectConsItem records a key-value pair that is part of object construction expression.
|
|
type ObjectConsItem struct {
|
|
// The key.
|
|
Key Expression
|
|
// The value.
|
|
Value Expression
|
|
}
|
|
|
|
// ObjectConsExpression represents a semantically-analyzed object construction expression.
|
|
type ObjectConsExpression struct {
|
|
// The syntax node associated with the object construction expression.
|
|
Syntax *hclsyntax.ObjectConsExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.ObjectConsTokens
|
|
|
|
// The items that comprise the object construction expression.
|
|
Items []ObjectConsItem
|
|
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the object construction expression.
|
|
func (x *ObjectConsExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the object construction expression.
|
|
func (x *ObjectConsExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the object construction expression.
|
|
func (x *ObjectConsExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *ObjectConsExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
var keys []Expression
|
|
for _, item := range x.Items {
|
|
if typecheckOperands {
|
|
keyDiags := item.Key.Typecheck(true)
|
|
diagnostics = append(diagnostics, keyDiags...)
|
|
|
|
valDiags := item.Value.Typecheck(true)
|
|
diagnostics = append(diagnostics, valDiags...)
|
|
}
|
|
|
|
keys = append(keys, item.Key)
|
|
if !InputType(StringType).ConversionFrom(item.Key.Type()).Exists() {
|
|
diagnostics = append(diagnostics, objectKeysMustBeStrings(item.Key))
|
|
}
|
|
}
|
|
|
|
// Attempt to build an object type out of the result. If there are any attribute names that come from variables,
|
|
// type the result as map(unify(propertyTypes)).
|
|
properties, isMapType, types := map[string]Type{}, false, []Type{}
|
|
for _, item := range x.Items {
|
|
types = append(types, item.Value.Type())
|
|
|
|
key := item.Key
|
|
if template, ok := key.(*TemplateExpression); ok && len(template.Parts) == 1 {
|
|
key = template.Parts[0]
|
|
}
|
|
|
|
keyLit, ok := key.(*LiteralValueExpression)
|
|
if ok {
|
|
key, err := convert.Convert(keyLit.Value, cty.String)
|
|
if err == nil {
|
|
properties[key.AsString()] = item.Value.Type()
|
|
continue
|
|
}
|
|
}
|
|
isMapType = true
|
|
}
|
|
var typ Type
|
|
if isMapType {
|
|
elementType, _ := UnifyTypes(types...)
|
|
typ = NewMapType(elementType)
|
|
} else {
|
|
typ = NewObjectType(properties)
|
|
}
|
|
|
|
x.exprType = liftOperationType(typ, keys...)
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *ObjectConsExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.ObjectConsExpr{
|
|
Items: make([]hclsyntax.ObjectConsItem, len(x.Items)),
|
|
}
|
|
for i, item := range x.Items {
|
|
syntax.Items[i] = hclsyntax.ObjectConsItem{
|
|
KeyExpr: &syntaxExpr{expr: item.Key},
|
|
ValueExpr: &syntaxExpr{expr: item.Value},
|
|
}
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *ObjectConsExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *ObjectConsExpression) HasTrailingTrivia() bool {
|
|
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *ObjectConsExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetOpenBrace(len(x.Items)))
|
|
}
|
|
|
|
func (x *ObjectConsExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewObjectConsTokens(len(x.Items))
|
|
}
|
|
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.OpenBrace, t)
|
|
}
|
|
|
|
func (x *ObjectConsExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetCloseBrace())
|
|
}
|
|
|
|
func (x *ObjectConsExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewObjectConsTokens(len(x.Items))
|
|
}
|
|
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.CloseBrace, t)
|
|
}
|
|
|
|
func (x *ObjectConsExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *ObjectConsExpression) print(w io.Writer, p *printer) {
|
|
// Print the opening brace.
|
|
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Tokens.GetOpenBrace(len(x.Items)))
|
|
|
|
// Print the items.
|
|
isMultiLine, trailingNewline := false, false
|
|
p.indented(func() {
|
|
items := x.Tokens.GetItems(len(x.Items))
|
|
for i, item := range x.Items {
|
|
tokens := syntax.NewObjectConsItemTokens(i == len(x.Items)-1)
|
|
if i < len(items) {
|
|
tokens = items[i]
|
|
}
|
|
|
|
if item.Key.HasLeadingTrivia() {
|
|
if _, i := item.Key.GetLeadingTrivia().Index("\n"); i != -1 {
|
|
isMultiLine = true
|
|
}
|
|
} else if len(items) > 1 {
|
|
isMultiLine = true
|
|
p.fprintf(w, "\n%s", p.indent)
|
|
}
|
|
p.fprintf(w, "%v% v% v", item.Key, tokens.Equals, item.Value)
|
|
|
|
if tokens.Comma != nil {
|
|
p.fprintf(w, "%v", tokens.Comma)
|
|
}
|
|
|
|
if isMultiLine && i == len(items)-1 {
|
|
trailingTrivia := item.Value.GetTrailingTrivia()
|
|
if tokens.Comma != nil {
|
|
trailingTrivia = tokens.Comma.TrailingTrivia
|
|
}
|
|
trailingNewline = trailingTrivia.EndsOnNewLine()
|
|
}
|
|
}
|
|
|
|
if len(x.Items) < len(items) {
|
|
for _, item := range items[len(x.Items):] {
|
|
p.fprintf(w, "%v", item.Equals.AllTrivia().CollapseWhitespace())
|
|
if item.Comma != nil {
|
|
p.fprintf(w, "%v", item.Comma.AllTrivia().CollapseWhitespace())
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
if x.Tokens != nil {
|
|
pre := ""
|
|
if isMultiLine && !trailingNewline {
|
|
pre = "\n" + p.indent
|
|
}
|
|
p.fprintf(w, "%s%v%)", pre, x.Tokens.CloseBrace, x.Tokens.Parentheses)
|
|
} else {
|
|
p.fprintf(w, "\n%s}", p.indent)
|
|
}
|
|
}
|
|
|
|
func (*ObjectConsExpression) isExpression() {}
|
|
|
|
func getTraverserTrivia(tokens syntax.TraverserTokens) (syntax.TriviaList, syntax.TriviaList) {
|
|
var leading, trailing syntax.TriviaList
|
|
switch tokens := tokens.(type) {
|
|
case *syntax.DotTraverserTokens:
|
|
leading = getExprLeadingTrivia(tokens.Parentheses, tokens.Dot)
|
|
trailing = getExprTrailingTrivia(tokens.Parentheses, tokens.Index)
|
|
case *syntax.BracketTraverserTokens:
|
|
leading = getExprLeadingTrivia(tokens.Parentheses, tokens.OpenBracket)
|
|
trailing = getExprTrailingTrivia(tokens.Parentheses, tokens.CloseBracket)
|
|
}
|
|
return leading, trailing
|
|
}
|
|
|
|
func setTraverserTrailingTrivia(tokens syntax.TraverserTokens, t syntax.TriviaList) {
|
|
switch tokens := tokens.(type) {
|
|
case *syntax.DotTraverserTokens:
|
|
setExprTrailingTrivia(tokens.Parentheses, &tokens.Index, t)
|
|
case *syntax.BracketTraverserTokens:
|
|
setExprTrailingTrivia(tokens.Parentheses, &tokens.CloseBracket, t)
|
|
default:
|
|
panic(fmt.Errorf("unexpected traverser of type %T", tokens))
|
|
}
|
|
}
|
|
|
|
func printTraverser(w io.Writer, p *printer, t hcl.Traverser, tokens syntax.TraverserTokens) {
|
|
var index string
|
|
switch t := t.(type) {
|
|
case hcl.TraverseAttr:
|
|
index = t.Name
|
|
case hcl.TraverseIndex:
|
|
index = literalText(t.Key, nil, true, true)
|
|
default:
|
|
panic(fmt.Errorf("unexpected traverser of type %T", t))
|
|
}
|
|
|
|
switch tokens := tokens.(type) {
|
|
case *syntax.DotTraverserTokens:
|
|
p.fprintf(w, "%(%v%v%)",
|
|
tokens.Parentheses,
|
|
tokens.Dot, identToken(tokens.Index, index),
|
|
tokens.Parentheses)
|
|
case *syntax.BracketTraverserTokens:
|
|
p.fprintf(w, "%(%v%v%v%)",
|
|
tokens.Parentheses,
|
|
tokens.OpenBracket, identToken(tokens.Index, index), tokens.CloseBracket,
|
|
tokens.Parentheses)
|
|
default:
|
|
panic(fmt.Errorf("unexpected traverser tokens of type %T", tokens))
|
|
}
|
|
}
|
|
|
|
func printRelativeTraversal(w io.Writer, p *printer, traversal hcl.Traversal, tokens []syntax.TraverserTokens) {
|
|
for i, traverser := range traversal {
|
|
// Fetch the traversal tokens.
|
|
var traverserTokens syntax.TraverserTokens
|
|
if i < len(tokens) {
|
|
traverserTokens = tokens[i]
|
|
}
|
|
printTraverser(w, p, traverser, traverserTokens)
|
|
}
|
|
|
|
// Print any remaining trivia.
|
|
if len(traversal) < len(tokens) {
|
|
for _, tokens := range tokens[len(traversal):] {
|
|
var trivia syntax.TriviaList
|
|
switch tokens := tokens.(type) {
|
|
case *syntax.DotTraverserTokens:
|
|
trivia = tokens.Dot.LeadingTrivia
|
|
trivia = append(trivia, tokens.Dot.TrailingTrivia...)
|
|
trivia = append(trivia, tokens.Index.LeadingTrivia...)
|
|
trivia = append(trivia, tokens.Index.TrailingTrivia...)
|
|
case *syntax.BracketTraverserTokens:
|
|
trivia = tokens.OpenBracket.LeadingTrivia
|
|
trivia = append(trivia, tokens.OpenBracket.TrailingTrivia...)
|
|
trivia = append(trivia, tokens.Index.LeadingTrivia...)
|
|
trivia = append(trivia, tokens.Index.TrailingTrivia...)
|
|
trivia = append(trivia, tokens.CloseBracket.LeadingTrivia...)
|
|
trivia = append(trivia, tokens.CloseBracket.TrailingTrivia...)
|
|
}
|
|
p.fprintf(w, "%v", trivia)
|
|
}
|
|
}
|
|
}
|
|
|
|
// RelativeTraversalExpression represents a semantically-analyzed relative traversal expression.
|
|
type RelativeTraversalExpression struct {
|
|
// The syntax node associated with the relative traversal expression.
|
|
Syntax *hclsyntax.RelativeTraversalExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.RelativeTraversalTokens
|
|
|
|
// The expression that computes the value being traversed.
|
|
Source Expression
|
|
// The traversal's parts.
|
|
Parts []Traversable
|
|
|
|
// The traversers.
|
|
Traversal hcl.Traversal
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the relative traversal expression.
|
|
func (x *RelativeTraversalExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the relative traversal expression.
|
|
func (x *RelativeTraversalExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the relative traversal expression.
|
|
func (x *RelativeTraversalExpression) Type() Type {
|
|
return GetTraversableType(x.Parts[len(x.Parts)-1])
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) typecheck(typecheckOperands, allowMissingVariables bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
if typecheckOperands {
|
|
sourceDiags := x.Source.Typecheck(true)
|
|
diagnostics = append(diagnostics, sourceDiags...)
|
|
}
|
|
|
|
parts, partDiags := bindTraversalParts(x.Source.Type(), x.Traversal, allowMissingVariables)
|
|
diagnostics = append(diagnostics, partDiags...)
|
|
|
|
x.Parts = parts
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
return x.typecheck(typecheckOperands, false)
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.RelativeTraversalExpr{
|
|
Source: &syntaxExpr{expr: x.Source},
|
|
Traversal: x.Traversal,
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Source)
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) HasTrailingTrivia() bool {
|
|
if parens := x.Tokens.GetParentheses(); parens.Any() {
|
|
return true
|
|
}
|
|
if x.Tokens != nil && len(x.Tokens.Traversal) > 0 {
|
|
return true
|
|
}
|
|
return x.Source.HasTrailingTrivia()
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Source)
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewRelativeTraversalTokens(x.Traversal)
|
|
}
|
|
setExprLeadingTrivia(x.Tokens.Parentheses, x.Source, t)
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
if parens := x.Tokens.GetParentheses(); parens.Any() {
|
|
return parens.GetTrailingTrivia()
|
|
}
|
|
if traversal := x.Tokens.GetTraversal(x.Traversal); len(traversal) > 0 {
|
|
_, trailingTrivia := getTraverserTrivia(traversal[len(traversal)-1])
|
|
return trailingTrivia
|
|
}
|
|
return x.Source.GetTrailingTrivia()
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewRelativeTraversalTokens(x.Traversal)
|
|
}
|
|
if parens := x.Tokens.GetParentheses(); parens.Any() {
|
|
parens.SetTrailingTrivia(t)
|
|
return
|
|
}
|
|
if len(x.Tokens.Traversal) > 0 {
|
|
setTraverserTrailingTrivia(x.Tokens.Traversal[len(x.Tokens.Traversal)-1], t)
|
|
return
|
|
}
|
|
x.Source.SetTrailingTrivia(t)
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *RelativeTraversalExpression) print(w io.Writer, p *printer) {
|
|
// Print the source expression.
|
|
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Source)
|
|
|
|
// Print the traversal.
|
|
printRelativeTraversal(w, p, x.Traversal, x.Tokens.GetTraversal(x.Traversal))
|
|
|
|
// Print the closing parentheses, if any.
|
|
p.fprintf(w, "%)", x.Tokens.GetParentheses())
|
|
}
|
|
|
|
func (*RelativeTraversalExpression) isExpression() {}
|
|
|
|
// ScopeTraversalExpression represents a semantically-analyzed scope traversal expression.
|
|
type ScopeTraversalExpression struct {
|
|
// The syntax node associated with the scope traversal expression.
|
|
Syntax *hclsyntax.ScopeTraversalExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.ScopeTraversalTokens
|
|
|
|
// The traversal's parts.
|
|
Parts []Traversable
|
|
|
|
// The root name.
|
|
RootName string
|
|
// The traversers.
|
|
Traversal hcl.Traversal
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the scope traversal expression.
|
|
func (x *ScopeTraversalExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the scope traversal expression.
|
|
func (x *ScopeTraversalExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the scope traversal expression.
|
|
func (x *ScopeTraversalExpression) Type() Type {
|
|
return GetTraversableType(x.Parts[len(x.Parts)-1])
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) typecheck(typecheckOperands, allowMissingVariables bool) hcl.Diagnostics {
|
|
parts, diagnostics := bindTraversalParts(x.Parts[0], x.Traversal.SimpleSplit().Rel, allowMissingVariables)
|
|
x.Parts = parts
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
return x.typecheck(typecheckOperands, false)
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
root, hasValue := x.Parts[0].(ValueTraversable)
|
|
if !hasValue {
|
|
return cty.UnknownVal(cty.DynamicPseudoType), nil
|
|
}
|
|
|
|
rootValue, diags := root.Value(context)
|
|
if diags.HasErrors() {
|
|
return cty.NilVal, diags
|
|
}
|
|
diagnostics = append(diagnostics, diags...)
|
|
|
|
if len(x.Traversal) == 1 {
|
|
return rootValue, diagnostics
|
|
}
|
|
return x.Traversal[1:].TraverseRel(rootValue)
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) HasTrailingTrivia() bool {
|
|
if parens := x.Tokens.GetParentheses(); parens.Any() {
|
|
return true
|
|
}
|
|
if x.Tokens != nil && len(x.Tokens.Traversal) > 0 {
|
|
return true
|
|
}
|
|
return x.Tokens != nil
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetRoot(x.Traversal))
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewScopeTraversalTokens(x.Traversal)
|
|
}
|
|
x.Tokens.Root.LeadingTrivia = t
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
if parens := x.Tokens.GetParentheses(); parens.Any() {
|
|
return parens.GetTrailingTrivia()
|
|
}
|
|
if traversal := x.Tokens.GetTraversal(x.Traversal); len(traversal) > 0 {
|
|
_, trailingTrivia := getTraverserTrivia(traversal[len(traversal)-1])
|
|
return trailingTrivia
|
|
}
|
|
return x.Tokens.GetRoot(x.Traversal).TrailingTrivia
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewScopeTraversalTokens(x.Traversal)
|
|
}
|
|
if parens := x.Tokens.GetParentheses(); parens.Any() {
|
|
parens.SetTrailingTrivia(t)
|
|
return
|
|
}
|
|
if len(x.Tokens.Traversal) > 0 {
|
|
setTraverserTrailingTrivia(x.Tokens.Traversal[len(x.Tokens.Traversal)-1], t)
|
|
return
|
|
}
|
|
x.Tokens.Root.TrailingTrivia = t
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *ScopeTraversalExpression) print(w io.Writer, p *printer) {
|
|
// Print the root name.
|
|
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Tokens.GetRoot(x.Traversal))
|
|
|
|
// Print the traversal.
|
|
printRelativeTraversal(w, p, x.Traversal[1:], x.Tokens.GetTraversal(x.Traversal))
|
|
|
|
// Print the closing parentheses, if any.
|
|
p.fprintf(w, "%)", x.Tokens.GetParentheses())
|
|
}
|
|
|
|
func (*ScopeTraversalExpression) isExpression() {}
|
|
|
|
type SplatVariable struct {
|
|
Variable
|
|
|
|
symbol hclsyntax.AnonSymbolExpr
|
|
}
|
|
|
|
func (v *SplatVariable) Value(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
return (&v.symbol).Value(context)
|
|
}
|
|
|
|
// SplatExpression represents a semantically-analyzed splat expression.
|
|
type SplatExpression struct {
|
|
// The syntax node associated with the splat expression.
|
|
Syntax *hclsyntax.SplatExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.SplatTokens
|
|
|
|
// The expression being splatted.
|
|
Source Expression
|
|
// The expression applied to each element of the splat.
|
|
Each Expression
|
|
// The local variable definition associated with the current item being processed. This definition is not part of
|
|
// a scope, and can only be referenced by an AnonSymbolExpr.
|
|
Item *SplatVariable
|
|
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the splat expression.
|
|
func (x *SplatExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the splat expression.
|
|
func (x *SplatExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the splat expression.
|
|
func (x *SplatExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func splatItemType(source Expression, splatSyntax *hclsyntax.SplatExpr) (Expression, Type) {
|
|
sourceType := unwrapIterableSourceType(source.Type())
|
|
itemType := sourceType
|
|
switch sourceType := sourceType.(type) {
|
|
case *ListType:
|
|
itemType = sourceType.ElementType
|
|
case *SetType:
|
|
itemType = sourceType.ElementType
|
|
case *TupleType:
|
|
itemType, _ = UnifyTypes(sourceType.ElementTypes...)
|
|
default:
|
|
if sourceType != DynamicType {
|
|
var tupleSyntax *hclsyntax.TupleConsExpr
|
|
if splatSyntax != nil {
|
|
tupleSyntax = &hclsyntax.TupleConsExpr{
|
|
Exprs: []hclsyntax.Expression{splatSyntax.Source},
|
|
SrcRange: splatSyntax.Source.Range(),
|
|
OpenRange: splatSyntax.Source.StartRange(),
|
|
}
|
|
}
|
|
|
|
source = &TupleConsExpression{
|
|
Syntax: tupleSyntax,
|
|
Tokens: syntax.NewTupleConsTokens(1),
|
|
Expressions: []Expression{source},
|
|
exprType: NewListType(source.Type()),
|
|
}
|
|
}
|
|
}
|
|
return source, itemType
|
|
}
|
|
|
|
func (x *SplatExpression) typecheck(retypeItem, typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
if typecheckOperands {
|
|
sourceDiags := x.Source.Typecheck(true)
|
|
diagnostics = append(diagnostics, sourceDiags...)
|
|
}
|
|
|
|
if retypeItem {
|
|
x.Source, x.Item.VariableType = splatItemType(x.Source, x.Syntax)
|
|
}
|
|
|
|
if typecheckOperands {
|
|
eachDiags := x.Each.Typecheck(true)
|
|
diagnostics = append(diagnostics, eachDiags...)
|
|
}
|
|
|
|
x.exprType = wrapIterableResultType(x.Source.Type(), NewListType(x.Each.Type()))
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *SplatExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
return x.typecheck(true, typecheckOperands)
|
|
}
|
|
|
|
func (x *SplatExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.SplatExpr{
|
|
Source: &syntaxExpr{expr: x.Source},
|
|
Each: &syntaxExpr{expr: x.Each},
|
|
Item: &x.Item.symbol,
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *SplatExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Source)
|
|
}
|
|
|
|
func (x *SplatExpression) HasTrailingTrivia() bool {
|
|
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Each)
|
|
}
|
|
|
|
func (x *SplatExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Source)
|
|
}
|
|
|
|
func (x *SplatExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewSplatTokens(false)
|
|
}
|
|
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.Open, t)
|
|
}
|
|
|
|
func (x *SplatExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
if parens := x.Tokens.GetParentheses(); parens.Any() {
|
|
return parens.GetTrailingTrivia()
|
|
}
|
|
if close := x.Tokens.GetClose(); close != nil {
|
|
return close.TrailingTrivia
|
|
}
|
|
return x.Tokens.GetStar().TrailingTrivia
|
|
}
|
|
|
|
func (x *SplatExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewSplatTokens(false)
|
|
}
|
|
if x.Tokens.Parentheses.Any() {
|
|
x.Tokens.Parentheses.SetTrailingTrivia(t)
|
|
return
|
|
}
|
|
if x.Tokens.Close == nil {
|
|
x.Tokens.Star.TrailingTrivia = t
|
|
return
|
|
}
|
|
x.Tokens.Close.TrailingTrivia = t
|
|
}
|
|
|
|
func (x *SplatExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *SplatExpression) print(w io.Writer, p *printer) {
|
|
isDot := x.Tokens.GetClose() == nil
|
|
|
|
p.fprintf(w, "%(%v%v%v", x.Tokens.GetParentheses(), x.Source, x.Tokens.GetOpen(), x.Tokens.GetStar())
|
|
if !isDot {
|
|
p.fprintf(w, "%v", x.Tokens.GetClose())
|
|
}
|
|
p.fprintf(w, "%v%)", x.Each, x.Tokens.GetParentheses())
|
|
}
|
|
|
|
func (*SplatExpression) isExpression() {}
|
|
|
|
// TemplateExpression represents a semantically-analyzed template expression.
|
|
type TemplateExpression struct {
|
|
// The syntax node associated with the template expression.
|
|
Syntax *hclsyntax.TemplateExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.TemplateTokens
|
|
|
|
// The parts of the template expression.
|
|
Parts []Expression
|
|
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the template expression.
|
|
func (x *TemplateExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the template expression.
|
|
func (x *TemplateExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the template expression.
|
|
func (x *TemplateExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *TemplateExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
if typecheckOperands {
|
|
for _, part := range x.Parts {
|
|
partDiags := part.Typecheck(true)
|
|
diagnostics = append(diagnostics, partDiags...)
|
|
}
|
|
}
|
|
|
|
x.exprType = liftOperationType(StringType, x.Parts...)
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *TemplateExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.TemplateExpr{
|
|
Parts: make([]hclsyntax.Expression, len(x.Parts)),
|
|
}
|
|
for i, p := range x.Parts {
|
|
syntax.Parts[i] = &syntaxExpr{expr: p}
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *TemplateExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *TemplateExpression) HasTrailingTrivia() bool {
|
|
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *TemplateExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetOpen())
|
|
}
|
|
|
|
func (x *TemplateExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewTemplateTokens()
|
|
}
|
|
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.Open, t)
|
|
}
|
|
|
|
func (x *TemplateExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetClose())
|
|
}
|
|
|
|
func (x *TemplateExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewTemplateTokens()
|
|
}
|
|
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.Close, t)
|
|
}
|
|
|
|
func (x *TemplateExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *TemplateExpression) print(w io.Writer, p *printer) {
|
|
// Print the opening quote.
|
|
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Tokens.GetOpen())
|
|
|
|
isHeredoc := x.Tokens.GetOpen().Raw.Type == hclsyntax.TokenOHeredoc
|
|
|
|
// Print the expressions.
|
|
for _, part := range x.Parts {
|
|
if lit, ok := part.(*LiteralValueExpression); ok && StringType.AssignableFrom(lit.Type()) {
|
|
lit.printLit(w, p, !isHeredoc)
|
|
} else {
|
|
p.fprintf(w, "%v", part)
|
|
}
|
|
}
|
|
|
|
// Print the closing quote
|
|
p.fprintf(w, "%v%)", x.Tokens.GetClose(), x.Tokens.GetParentheses())
|
|
}
|
|
|
|
func (*TemplateExpression) isExpression() {}
|
|
|
|
// TemplateJoinExpression represents a semantically-analyzed template join expression.
|
|
type TemplateJoinExpression struct {
|
|
// The syntax node associated with the template join expression.
|
|
Syntax *hclsyntax.TemplateJoinExpr
|
|
|
|
// The tuple being joined.
|
|
Tuple Expression
|
|
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the template join expression.
|
|
func (x *TemplateJoinExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the template join expression.
|
|
func (x *TemplateJoinExpression) NodeTokens() syntax.NodeTokens {
|
|
return nil
|
|
}
|
|
|
|
// Type returns the type of the template join expression.
|
|
func (x *TemplateJoinExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *TemplateJoinExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
if typecheckOperands {
|
|
tupleDiags := x.Tuple.Typecheck(true)
|
|
diagnostics = append(diagnostics, tupleDiags...)
|
|
}
|
|
|
|
x.exprType = liftOperationType(StringType, x.Tuple)
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *TemplateJoinExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.TemplateJoinExpr{
|
|
Tuple: &syntaxExpr{expr: x.Tuple},
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *TemplateJoinExpression) HasLeadingTrivia() bool {
|
|
return x.Tuple.HasLeadingTrivia()
|
|
}
|
|
|
|
func (x *TemplateJoinExpression) HasTrailingTrivia() bool {
|
|
return x.Tuple.HasTrailingTrivia()
|
|
}
|
|
|
|
func (x *TemplateJoinExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return x.Tuple.GetLeadingTrivia()
|
|
}
|
|
|
|
func (x *TemplateJoinExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
x.Tuple.SetLeadingTrivia(t)
|
|
}
|
|
|
|
func (x *TemplateJoinExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
return x.Tuple.GetTrailingTrivia()
|
|
}
|
|
|
|
func (x *TemplateJoinExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
x.Tuple.SetTrailingTrivia(t)
|
|
}
|
|
|
|
func (x *TemplateJoinExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *TemplateJoinExpression) print(w io.Writer, p *printer) {
|
|
p.fprintf(w, "%v", x.Tuple)
|
|
}
|
|
|
|
func (*TemplateJoinExpression) isExpression() {}
|
|
|
|
// TupleConsExpression represents a semantically-analyzed tuple construction expression.
|
|
type TupleConsExpression struct {
|
|
// The syntax node associated with the tuple construction expression.
|
|
Syntax *hclsyntax.TupleConsExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.TupleConsTokens
|
|
|
|
// The elements of the tuple.
|
|
Expressions []Expression
|
|
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the tuple construction expression.
|
|
func (x *TupleConsExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the tuple construction expression.
|
|
func (x *TupleConsExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// Type returns the type of the tuple construction expression.
|
|
func (x *TupleConsExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *TupleConsExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
elementTypes := make([]Type, len(x.Expressions))
|
|
for i, expr := range x.Expressions {
|
|
if typecheckOperands {
|
|
exprDiags := expr.Typecheck(true)
|
|
diagnostics = append(diagnostics, exprDiags...)
|
|
}
|
|
|
|
elementTypes[i] = expr.Type()
|
|
}
|
|
|
|
x.exprType = NewTupleType(elementTypes...)
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *TupleConsExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.TupleConsExpr{
|
|
Exprs: make([]hclsyntax.Expression, len(x.Expressions)),
|
|
}
|
|
for i, x := range x.Expressions {
|
|
syntax.Exprs[i] = &syntaxExpr{expr: x}
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *TupleConsExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *TupleConsExpression) HasTrailingTrivia() bool {
|
|
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *TupleConsExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetOpenBracket())
|
|
}
|
|
|
|
func (x *TupleConsExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewTupleConsTokens(len(x.Expressions))
|
|
}
|
|
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.OpenBracket, t)
|
|
}
|
|
|
|
func (x *TupleConsExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetCloseBracket())
|
|
}
|
|
|
|
func (x *TupleConsExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewTupleConsTokens(len(x.Expressions))
|
|
}
|
|
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.CloseBracket, t)
|
|
}
|
|
|
|
func (x *TupleConsExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *TupleConsExpression) print(w io.Writer, p *printer) {
|
|
// Print the opening bracket.
|
|
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Tokens.GetOpenBracket())
|
|
|
|
// Print each element and its comma.
|
|
commas := x.Tokens.GetCommas(len(x.Expressions))
|
|
p.indented(func() {
|
|
for i, expr := range x.Expressions {
|
|
if !expr.HasLeadingTrivia() {
|
|
p.fprintf(w, "\n%s", p.indent)
|
|
}
|
|
p.fprintf(w, "%v", expr)
|
|
|
|
if i != len(x.Expressions)-1 {
|
|
var comma syntax.Token
|
|
if i < len(commas) {
|
|
comma = commas[i]
|
|
}
|
|
p.fprintf(w, "%v", comma)
|
|
}
|
|
}
|
|
|
|
// If there were commas left over, print the trivia for each.
|
|
//
|
|
// TODO(pdg): filter to only comments?
|
|
if len(x.Expressions) > 0 && len(x.Expressions)-1 <= len(commas) {
|
|
for _, comma := range commas[len(x.Expressions)-1:] {
|
|
p.fprintf(w, "%v", comma.AllTrivia().CollapseWhitespace())
|
|
}
|
|
}
|
|
})
|
|
|
|
// Print the closing bracket.
|
|
if x.Tokens != nil {
|
|
p.fprintf(w, "%v%)", x.Tokens.CloseBracket, x.Tokens.GetParentheses())
|
|
} else {
|
|
p.fprintf(w, "\n%s]", p.indent)
|
|
}
|
|
}
|
|
|
|
func (*TupleConsExpression) isExpression() {}
|
|
|
|
// UnaryOpExpression represents a semantically-analyzed unary operation.
|
|
type UnaryOpExpression struct {
|
|
// The syntax node associated with the unary operation.
|
|
Syntax *hclsyntax.UnaryOpExpr
|
|
// The tokens associated with the expression, if any.
|
|
Tokens *syntax.UnaryOpTokens
|
|
|
|
// The operation.
|
|
Operation *hclsyntax.Operation
|
|
// The operand of the operation.
|
|
Operand Expression
|
|
|
|
operandType Type
|
|
exprType Type
|
|
}
|
|
|
|
// SyntaxNode returns the syntax node associated with the unary operation.
|
|
func (x *UnaryOpExpression) SyntaxNode() hclsyntax.Node {
|
|
return x.Syntax
|
|
}
|
|
|
|
// NodeTokens returns the tokens associated with the unary operation.
|
|
func (x *UnaryOpExpression) NodeTokens() syntax.NodeTokens {
|
|
return x.Tokens
|
|
}
|
|
|
|
// OperandType returns the operand type of the unary operation.
|
|
func (x *UnaryOpExpression) OperandType() Type {
|
|
return x.operandType
|
|
}
|
|
|
|
// Type returns the type of the unary operation.
|
|
func (x *UnaryOpExpression) Type() Type {
|
|
return x.exprType
|
|
}
|
|
|
|
func (x *UnaryOpExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
if typecheckOperands {
|
|
operandDiags := x.Operand.Typecheck(true)
|
|
diagnostics = append(diagnostics, operandDiags...)
|
|
}
|
|
|
|
// Compute the signature for the operator and typecheck the arguments.
|
|
signature := getOperationSignature(x.Operation)
|
|
contract.Assert(len(signature.Parameters) == 1)
|
|
|
|
x.operandType = signature.Parameters[0].Type
|
|
|
|
var rng hcl.Range
|
|
if x.Syntax != nil {
|
|
rng = x.Syntax.Range()
|
|
}
|
|
typecheckDiags := typecheckArgs(rng, signature, x.Operand)
|
|
diagnostics = append(diagnostics, typecheckDiags...)
|
|
|
|
x.exprType = liftOperationType(signature.ReturnType, x.Operand)
|
|
return diagnostics
|
|
}
|
|
|
|
func (x *UnaryOpExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
syntax := &hclsyntax.UnaryOpExpr{
|
|
Op: x.Operation,
|
|
Val: &syntaxExpr{expr: x.Operand},
|
|
}
|
|
return syntax.Value(context)
|
|
}
|
|
|
|
func (x *UnaryOpExpression) HasLeadingTrivia() bool {
|
|
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
|
|
}
|
|
|
|
func (x *UnaryOpExpression) HasTrailingTrivia() bool {
|
|
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Operand)
|
|
}
|
|
|
|
func (x *UnaryOpExpression) GetLeadingTrivia() syntax.TriviaList {
|
|
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetOperator(x.Operation))
|
|
}
|
|
|
|
func (x *UnaryOpExpression) SetLeadingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewUnaryOpTokens(x.Operation)
|
|
}
|
|
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.Operator, t)
|
|
}
|
|
|
|
func (x *UnaryOpExpression) GetTrailingTrivia() syntax.TriviaList {
|
|
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Operand)
|
|
}
|
|
|
|
func (x *UnaryOpExpression) SetTrailingTrivia(t syntax.TriviaList) {
|
|
if x.Tokens == nil {
|
|
x.Tokens = syntax.NewUnaryOpTokens(x.Operation)
|
|
}
|
|
setExprTrailingTrivia(x.Tokens.Parentheses, x.Operand, t)
|
|
}
|
|
|
|
func (x *UnaryOpExpression) Format(f fmt.State, c rune) {
|
|
x.print(f, &printer{})
|
|
}
|
|
|
|
func (x *UnaryOpExpression) print(w io.Writer, p *printer) {
|
|
precedence := operatorPrecedence(x.Operation)
|
|
p.fprintf(w, "%[2](%[3]v%.[1]*[4]v%[5])",
|
|
precedence,
|
|
x.Tokens.GetParentheses(),
|
|
x.Tokens.GetOperator(x.Operation), x.Operand,
|
|
x.Tokens.GetParentheses())
|
|
}
|
|
|
|
func (*UnaryOpExpression) isExpression() {}
|