aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/hcl2/hclwrite/ast_expression.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/hclwrite/ast_expression.go')
-rw-r--r--vendor/github.com/hashicorp/hcl2/hclwrite/ast_expression.go201
1 files changed, 201 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/hclwrite/ast_expression.go b/vendor/github.com/hashicorp/hcl2/hclwrite/ast_expression.go
new file mode 100644
index 0000000..62d89fb
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hclwrite/ast_expression.go
@@ -0,0 +1,201 @@
1package hclwrite
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/hcl2/hcl"
7 "github.com/hashicorp/hcl2/hcl/hclsyntax"
8 "github.com/zclconf/go-cty/cty"
9)
10
11type Expression struct {
12 inTree
13
14 absTraversals nodeSet
15}
16
17func newExpression() *Expression {
18 return &Expression{
19 inTree: newInTree(),
20 absTraversals: newNodeSet(),
21 }
22}
23
24// NewExpressionLiteral constructs an an expression that represents the given
25// literal value.
26//
27// Since an unknown value cannot be represented in source code, this function
28// will panic if the given value is unknown or contains a nested unknown value.
29// Use val.IsWhollyKnown before calling to be sure.
30//
31// HCL native syntax does not directly represent lists, maps, and sets, and
32// instead relies on the automatic conversions to those collection types from
33// either list or tuple constructor syntax. Therefore converting collection
34// values to source code and re-reading them will lose type information, and
35// the reader must provide a suitable type at decode time to recover the
36// original value.
37func NewExpressionLiteral(val cty.Value) *Expression {
38 toks := TokensForValue(val)
39 expr := newExpression()
40 expr.children.AppendUnstructuredTokens(toks)
41 return expr
42}
43
44// NewExpressionAbsTraversal constructs an expression that represents the
45// given traversal, which must be absolute or this function will panic.
46func NewExpressionAbsTraversal(traversal hcl.Traversal) *Expression {
47 if traversal.IsRelative() {
48 panic("can't construct expression from relative traversal")
49 }
50
51 physT := newTraversal()
52 rootName := traversal.RootName()
53 steps := traversal[1:]
54
55 {
56 tn := newTraverseName()
57 tn.name = tn.children.Append(newIdentifier(&Token{
58 Type: hclsyntax.TokenIdent,
59 Bytes: []byte(rootName),
60 }))
61 physT.steps.Add(physT.children.Append(tn))
62 }
63
64 for _, step := range steps {
65 switch ts := step.(type) {
66 case hcl.TraverseAttr:
67 tn := newTraverseName()
68 tn.children.AppendUnstructuredTokens(Tokens{
69 {
70 Type: hclsyntax.TokenDot,
71 Bytes: []byte{'.'},
72 },
73 })
74 tn.name = tn.children.Append(newIdentifier(&Token{
75 Type: hclsyntax.TokenIdent,
76 Bytes: []byte(ts.Name),
77 }))
78 physT.steps.Add(physT.children.Append(tn))
79 case hcl.TraverseIndex:
80 ti := newTraverseIndex()
81 ti.children.AppendUnstructuredTokens(Tokens{
82 {
83 Type: hclsyntax.TokenOBrack,
84 Bytes: []byte{'['},
85 },
86 })
87 indexExpr := NewExpressionLiteral(ts.Key)
88 ti.key = ti.children.Append(indexExpr)
89 ti.children.AppendUnstructuredTokens(Tokens{
90 {
91 Type: hclsyntax.TokenCBrack,
92 Bytes: []byte{']'},
93 },
94 })
95 physT.steps.Add(physT.children.Append(ti))
96 }
97 }
98
99 expr := newExpression()
100 expr.absTraversals.Add(expr.children.Append(physT))
101 return expr
102}
103
104// Variables returns the absolute traversals that exist within the receiving
105// expression.
106func (e *Expression) Variables() []*Traversal {
107 nodes := e.absTraversals.List()
108 ret := make([]*Traversal, len(nodes))
109 for i, node := range nodes {
110 ret[i] = node.content.(*Traversal)
111 }
112 return ret
113}
114
115// RenameVariablePrefix examines each of the absolute traversals in the
116// receiving expression to see if they have the given sequence of names as
117// a prefix prefix. If so, they are updated in place to have the given
118// replacement names instead of that prefix.
119//
120// This can be used to implement symbol renaming. The calling application can
121// visit all relevant expressions in its input and apply the same renaming
122// to implement a global symbol rename.
123//
124// The search and replacement traversals must be the same length, or this
125// method will panic. Only attribute access operations can be matched and
126// replaced. Index steps never match the prefix.
127func (e *Expression) RenameVariablePrefix(search, replacement []string) {
128 if len(search) != len(replacement) {
129 panic(fmt.Sprintf("search and replacement length mismatch (%d and %d)", len(search), len(replacement)))
130 }
131Traversals:
132 for node := range e.absTraversals {
133 traversal := node.content.(*Traversal)
134 if len(traversal.steps) < len(search) {
135 // If it's shorter then it can't have our prefix
136 continue
137 }
138
139 stepNodes := traversal.steps.List()
140 for i, name := range search {
141 step, isName := stepNodes[i].content.(*TraverseName)
142 if !isName {
143 continue Traversals // only name nodes can match
144 }
145 foundNameBytes := step.name.content.(*identifier).token.Bytes
146 if len(foundNameBytes) != len(name) {
147 continue Traversals
148 }
149 if string(foundNameBytes) != name {
150 continue Traversals
151 }
152 }
153
154 // If we get here then the prefix matched, so now we'll swap in
155 // the replacement strings.
156 for i, name := range replacement {
157 step := stepNodes[i].content.(*TraverseName)
158 token := step.name.content.(*identifier).token
159 token.Bytes = []byte(name)
160 }
161 }
162}
163
164// Traversal represents a sequence of variable, attribute, and/or index
165// operations.
166type Traversal struct {
167 inTree
168
169 steps nodeSet
170}
171
172func newTraversal() *Traversal {
173 return &Traversal{
174 inTree: newInTree(),
175 steps: newNodeSet(),
176 }
177}
178
179type TraverseName struct {
180 inTree
181
182 name *node
183}
184
185func newTraverseName() *TraverseName {
186 return &TraverseName{
187 inTree: newInTree(),
188 }
189}
190
191type TraverseIndex struct {
192 inTree
193
194 key *node
195}
196
197func newTraverseIndex() *TraverseIndex {
198 return &TraverseIndex{
199 inTree: newInTree(),
200 }
201}