]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/hcl2/hclwrite/ast_expression.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / hcl2 / hclwrite / ast_expression.go
1 package hclwrite
2
3 import (
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
11 type Expression struct {
12 inTree
13
14 absTraversals nodeSet
15 }
16
17 func 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.
37 func 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.
46 func 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.
106 func (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.
127 func (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 }
131 Traversals:
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.
166 type Traversal struct {
167 inTree
168
169 steps nodeSet
170 }
171
172 func newTraversal() *Traversal {
173 return &Traversal{
174 inTree: newInTree(),
175 steps: newNodeSet(),
176 }
177 }
178
179 type TraverseName struct {
180 inTree
181
182 name *node
183 }
184
185 func newTraverseName() *TraverseName {
186 return &TraverseName{
187 inTree: newInTree(),
188 }
189 }
190
191 type TraverseIndex struct {
192 inTree
193
194 key *node
195 }
196
197 func newTraverseIndex() *TraverseIndex {
198 return &TraverseIndex{
199 inTree: newInTree(),
200 }
201 }