terraform-provider-google/vendor/github.com/hashicorp/hcl2/hclwrite/ast_expression.go
Paddy 961c878e0d Switch to using Go modules. (#2679)
Switch to using Go modules.

This migrates our vendor.json to use Go 1.11's modules system, and
replaces the vendor folder with the output of go mod vendor.

The vendored code should remain basically the same; I believe some
tree shaking of packages and support scripts/licenses/READMEs/etc.
happened.

This also fixes Travis and our Makefile to no longer use govendor.
2018-12-20 17:22:22 -08:00

202 lines
5.2 KiB
Go

package hclwrite
import (
"fmt"
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
"github.com/zclconf/go-cty/cty"
)
type Expression struct {
inTree
absTraversals nodeSet
}
func newExpression() *Expression {
return &Expression{
inTree: newInTree(),
absTraversals: newNodeSet(),
}
}
// NewExpressionLiteral constructs an an expression that represents the given
// literal value.
//
// Since an unknown value cannot be represented in source code, this function
// will panic if the given value is unknown or contains a nested unknown value.
// Use val.IsWhollyKnown before calling to be sure.
//
// HCL native syntax does not directly represent lists, maps, and sets, and
// instead relies on the automatic conversions to those collection types from
// either list or tuple constructor syntax. Therefore converting collection
// values to source code and re-reading them will lose type information, and
// the reader must provide a suitable type at decode time to recover the
// original value.
func NewExpressionLiteral(val cty.Value) *Expression {
toks := TokensForValue(val)
expr := newExpression()
expr.children.AppendUnstructuredTokens(toks)
return expr
}
// NewExpressionAbsTraversal constructs an expression that represents the
// given traversal, which must be absolute or this function will panic.
func NewExpressionAbsTraversal(traversal hcl.Traversal) *Expression {
if traversal.IsRelative() {
panic("can't construct expression from relative traversal")
}
physT := newTraversal()
rootName := traversal.RootName()
steps := traversal[1:]
{
tn := newTraverseName()
tn.name = tn.children.Append(newIdentifier(&Token{
Type: hclsyntax.TokenIdent,
Bytes: []byte(rootName),
}))
physT.steps.Add(physT.children.Append(tn))
}
for _, step := range steps {
switch ts := step.(type) {
case hcl.TraverseAttr:
tn := newTraverseName()
tn.children.AppendUnstructuredTokens(Tokens{
{
Type: hclsyntax.TokenDot,
Bytes: []byte{'.'},
},
})
tn.name = tn.children.Append(newIdentifier(&Token{
Type: hclsyntax.TokenIdent,
Bytes: []byte(ts.Name),
}))
physT.steps.Add(physT.children.Append(tn))
case hcl.TraverseIndex:
ti := newTraverseIndex()
ti.children.AppendUnstructuredTokens(Tokens{
{
Type: hclsyntax.TokenOBrack,
Bytes: []byte{'['},
},
})
indexExpr := NewExpressionLiteral(ts.Key)
ti.key = ti.children.Append(indexExpr)
ti.children.AppendUnstructuredTokens(Tokens{
{
Type: hclsyntax.TokenCBrack,
Bytes: []byte{']'},
},
})
physT.steps.Add(physT.children.Append(ti))
}
}
expr := newExpression()
expr.absTraversals.Add(expr.children.Append(physT))
return expr
}
// Variables returns the absolute traversals that exist within the receiving
// expression.
func (e *Expression) Variables() []*Traversal {
nodes := e.absTraversals.List()
ret := make([]*Traversal, len(nodes))
for i, node := range nodes {
ret[i] = node.content.(*Traversal)
}
return ret
}
// RenameVariablePrefix examines each of the absolute traversals in the
// receiving expression to see if they have the given sequence of names as
// a prefix prefix. If so, they are updated in place to have the given
// replacement names instead of that prefix.
//
// This can be used to implement symbol renaming. The calling application can
// visit all relevant expressions in its input and apply the same renaming
// to implement a global symbol rename.
//
// The search and replacement traversals must be the same length, or this
// method will panic. Only attribute access operations can be matched and
// replaced. Index steps never match the prefix.
func (e *Expression) RenameVariablePrefix(search, replacement []string) {
if len(search) != len(replacement) {
panic(fmt.Sprintf("search and replacement length mismatch (%d and %d)", len(search), len(replacement)))
}
Traversals:
for node := range e.absTraversals {
traversal := node.content.(*Traversal)
if len(traversal.steps) < len(search) {
// If it's shorter then it can't have our prefix
continue
}
stepNodes := traversal.steps.List()
for i, name := range search {
step, isName := stepNodes[i].content.(*TraverseName)
if !isName {
continue Traversals // only name nodes can match
}
foundNameBytes := step.name.content.(*identifier).token.Bytes
if len(foundNameBytes) != len(name) {
continue Traversals
}
if string(foundNameBytes) != name {
continue Traversals
}
}
// If we get here then the prefix matched, so now we'll swap in
// the replacement strings.
for i, name := range replacement {
step := stepNodes[i].content.(*TraverseName)
token := step.name.content.(*identifier).token
token.Bytes = []byte(name)
}
}
}
// Traversal represents a sequence of variable, attribute, and/or index
// operations.
type Traversal struct {
inTree
steps nodeSet
}
func newTraversal() *Traversal {
return &Traversal{
inTree: newInTree(),
steps: newNodeSet(),
}
}
type TraverseName struct {
inTree
name *node
}
func newTraverseName() *TraverseName {
return &TraverseName{
inTree: newInTree(),
}
}
type TraverseIndex struct {
inTree
key *node
}
func newTraverseIndex() *TraverseIndex {
return &TraverseIndex{
inTree: newInTree(),
}
}