aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/hil
diff options
context:
space:
mode:
authorJake Champlin <jake.champlin.27@gmail.com>2017-06-06 12:40:07 -0400
committerJake Champlin <jake.champlin.27@gmail.com>2017-06-06 12:40:07 -0400
commitbae9f6d2fd5eb5bc80929bd393932b23f14d7c93 (patch)
treeca9ab12a7d78b1fc27a8f734729081357ce6d252 /vendor/github.com/hashicorp/hil
parent254c495b6bebab3fb72a243c4bce858d79e6ee99 (diff)
downloadterraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.gz
terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.zst
terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.zip
Initial transfer of provider code
Diffstat (limited to 'vendor/github.com/hashicorp/hil')
-rw-r--r--vendor/github.com/hashicorp/hil/LICENSE353
-rw-r--r--vendor/github.com/hashicorp/hil/README.md102
-rw-r--r--vendor/github.com/hashicorp/hil/appveyor.yml18
-rw-r--r--vendor/github.com/hashicorp/hil/ast/arithmetic.go43
-rw-r--r--vendor/github.com/hashicorp/hil/ast/arithmetic_op.go24
-rw-r--r--vendor/github.com/hashicorp/hil/ast/ast.go99
-rw-r--r--vendor/github.com/hashicorp/hil/ast/call.go47
-rw-r--r--vendor/github.com/hashicorp/hil/ast/conditional.go36
-rw-r--r--vendor/github.com/hashicorp/hil/ast/index.go76
-rw-r--r--vendor/github.com/hashicorp/hil/ast/literal.go88
-rw-r--r--vendor/github.com/hashicorp/hil/ast/output.go78
-rw-r--r--vendor/github.com/hashicorp/hil/ast/scope.go90
-rw-r--r--vendor/github.com/hashicorp/hil/ast/stack.go25
-rw-r--r--vendor/github.com/hashicorp/hil/ast/type_string.go54
-rw-r--r--vendor/github.com/hashicorp/hil/ast/unknown.go30
-rw-r--r--vendor/github.com/hashicorp/hil/ast/variable_access.go36
-rw-r--r--vendor/github.com/hashicorp/hil/ast/variables_helper.go63
-rw-r--r--vendor/github.com/hashicorp/hil/builtins.go331
-rw-r--r--vendor/github.com/hashicorp/hil/check_identifier.go88
-rw-r--r--vendor/github.com/hashicorp/hil/check_types.go668
-rw-r--r--vendor/github.com/hashicorp/hil/convert.go159
-rw-r--r--vendor/github.com/hashicorp/hil/eval.go472
-rw-r--r--vendor/github.com/hashicorp/hil/eval_type.go16
-rw-r--r--vendor/github.com/hashicorp/hil/evaltype_string.go42
-rw-r--r--vendor/github.com/hashicorp/hil/parse.go29
-rw-r--r--vendor/github.com/hashicorp/hil/parser/binary_op.go45
-rw-r--r--vendor/github.com/hashicorp/hil/parser/error.go38
-rw-r--r--vendor/github.com/hashicorp/hil/parser/fuzz.go28
-rw-r--r--vendor/github.com/hashicorp/hil/parser/parser.go522
-rw-r--r--vendor/github.com/hashicorp/hil/scanner/peeker.go55
-rw-r--r--vendor/github.com/hashicorp/hil/scanner/scanner.go550
-rw-r--r--vendor/github.com/hashicorp/hil/scanner/token.go105
-rw-r--r--vendor/github.com/hashicorp/hil/scanner/tokentype_string.go51
-rw-r--r--vendor/github.com/hashicorp/hil/transform_fixed.go29
-rw-r--r--vendor/github.com/hashicorp/hil/walk.go266
35 files changed, 4756 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hil/LICENSE b/vendor/github.com/hashicorp/hil/LICENSE
new file mode 100644
index 0000000..82b4de9
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/LICENSE
@@ -0,0 +1,353 @@
1Mozilla Public License, version 2.0
2
31. Definitions
4
51.1. “Contributor”
6
7 means each individual or legal entity that creates, contributes to the
8 creation of, or owns Covered Software.
9
101.2. “Contributor Version”
11
12 means the combination of the Contributions of others (if any) used by a
13 Contributor and that particular Contributor’s Contribution.
14
151.3. “Contribution”
16
17 means Covered Software of a particular Contributor.
18
191.4. “Covered Software”
20
21 means Source Code Form to which the initial Contributor has attached the
22 notice in Exhibit A, the Executable Form of such Source Code Form, and
23 Modifications of such Source Code Form, in each case including portions
24 thereof.
25
261.5. “Incompatible With Secondary Licenses”
27 means
28
29 a. that the initial Contributor has attached the notice described in
30 Exhibit B to the Covered Software; or
31
32 b. that the Covered Software was made available under the terms of version
33 1.1 or earlier of the License, but not also under the terms of a
34 Secondary License.
35
361.6. “Executable Form”
37
38 means any form of the work other than Source Code Form.
39
401.7. “Larger Work”
41
42 means a work that combines Covered Software with other material, in a separate
43 file or files, that is not Covered Software.
44
451.8. “License”
46
47 means this document.
48
491.9. “Licensable”
50
51 means having the right to grant, to the maximum extent possible, whether at the
52 time of the initial grant or subsequently, any and all of the rights conveyed by
53 this License.
54
551.10. “Modifications”
56
57 means any of the following:
58
59 a. any file in Source Code Form that results from an addition to, deletion
60 from, or modification of the contents of Covered Software; or
61
62 b. any new file in Source Code Form that contains any Covered Software.
63
641.11. “Patent Claims” of a Contributor
65
66 means any patent claim(s), including without limitation, method, process,
67 and apparatus claims, in any patent Licensable by such Contributor that
68 would be infringed, but for the grant of the License, by the making,
69 using, selling, offering for sale, having made, import, or transfer of
70 either its Contributions or its Contributor Version.
71
721.12. “Secondary License”
73
74 means either the GNU General Public License, Version 2.0, the GNU Lesser
75 General Public License, Version 2.1, the GNU Affero General Public
76 License, Version 3.0, or any later versions of those licenses.
77
781.13. “Source Code Form”
79
80 means the form of the work preferred for making modifications.
81
821.14. “You” (or “Your”)
83
84 means an individual or a legal entity exercising rights under this
85 License. For legal entities, “You” includes any entity that controls, is
86 controlled by, or is under common control with You. For purposes of this
87 definition, “control” means (a) the power, direct or indirect, to cause
88 the direction or management of such entity, whether by contract or
89 otherwise, or (b) ownership of more than fifty percent (50%) of the
90 outstanding shares or beneficial ownership of such entity.
91
92
932. License Grants and Conditions
94
952.1. Grants
96
97 Each Contributor hereby grants You a world-wide, royalty-free,
98 non-exclusive license:
99
100 a. under intellectual property rights (other than patent or trademark)
101 Licensable by such Contributor to use, reproduce, make available,
102 modify, display, perform, distribute, and otherwise exploit its
103 Contributions, either on an unmodified basis, with Modifications, or as
104 part of a Larger Work; and
105
106 b. under Patent Claims of such Contributor to make, use, sell, offer for
107 sale, have made, import, and otherwise transfer either its Contributions
108 or its Contributor Version.
109
1102.2. Effective Date
111
112 The licenses granted in Section 2.1 with respect to any Contribution become
113 effective for each Contribution on the date the Contributor first distributes
114 such Contribution.
115
1162.3. Limitations on Grant Scope
117
118 The licenses granted in this Section 2 are the only rights granted under this
119 License. No additional rights or licenses will be implied from the distribution
120 or licensing of Covered Software under this License. Notwithstanding Section
121 2.1(b) above, no patent license is granted by a Contributor:
122
123 a. for any code that a Contributor has removed from Covered Software; or
124
125 b. for infringements caused by: (i) Your and any other third party’s
126 modifications of Covered Software, or (ii) the combination of its
127 Contributions with other software (except as part of its Contributor
128 Version); or
129
130 c. under Patent Claims infringed by Covered Software in the absence of its
131 Contributions.
132
133 This License does not grant any rights in the trademarks, service marks, or
134 logos of any Contributor (except as may be necessary to comply with the
135 notice requirements in Section 3.4).
136
1372.4. Subsequent Licenses
138
139 No Contributor makes additional grants as a result of Your choice to
140 distribute the Covered Software under a subsequent version of this License
141 (see Section 10.2) or under the terms of a Secondary License (if permitted
142 under the terms of Section 3.3).
143
1442.5. Representation
145
146 Each Contributor represents that the Contributor believes its Contributions
147 are its original creation(s) or it has sufficient rights to grant the
148 rights to its Contributions conveyed by this License.
149
1502.6. Fair Use
151
152 This License is not intended to limit any rights You have under applicable
153 copyright doctrines of fair use, fair dealing, or other equivalents.
154
1552.7. Conditions
156
157 Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
158 Section 2.1.
159
160
1613. Responsibilities
162
1633.1. Distribution of Source Form
164
165 All distribution of Covered Software in Source Code Form, including any
166 Modifications that You create or to which You contribute, must be under the
167 terms of this License. You must inform recipients that the Source Code Form
168 of the Covered Software is governed by the terms of this License, and how
169 they can obtain a copy of this License. You may not attempt to alter or
170 restrict the recipients’ rights in the Source Code Form.
171
1723.2. Distribution of Executable Form
173
174 If You distribute Covered Software in Executable Form then:
175
176 a. such Covered Software must also be made available in Source Code Form,
177 as described in Section 3.1, and You must inform recipients of the
178 Executable Form how they can obtain a copy of such Source Code Form by
179 reasonable means in a timely manner, at a charge no more than the cost
180 of distribution to the recipient; and
181
182 b. You may distribute such Executable Form under the terms of this License,
183 or sublicense it under different terms, provided that the license for
184 the Executable Form does not attempt to limit or alter the recipients’
185 rights in the Source Code Form under this License.
186
1873.3. Distribution of a Larger Work
188
189 You may create and distribute a Larger Work under terms of Your choice,
190 provided that You also comply with the requirements of this License for the
191 Covered Software. If the Larger Work is a combination of Covered Software
192 with a work governed by one or more Secondary Licenses, and the Covered
193 Software is not Incompatible With Secondary Licenses, this License permits
194 You to additionally distribute such Covered Software under the terms of
195 such Secondary License(s), so that the recipient of the Larger Work may, at
196 their option, further distribute the Covered Software under the terms of
197 either this License or such Secondary License(s).
198
1993.4. Notices
200
201 You may not remove or alter the substance of any license notices (including
202 copyright notices, patent notices, disclaimers of warranty, or limitations
203 of liability) contained within the Source Code Form of the Covered
204 Software, except that You may alter any license notices to the extent
205 required to remedy known factual inaccuracies.
206
2073.5. Application of Additional Terms
208
209 You may choose to offer, and to charge a fee for, warranty, support,
210 indemnity or liability obligations to one or more recipients of Covered
211 Software. However, You may do so only on Your own behalf, and not on behalf
212 of any Contributor. You must make it absolutely clear that any such
213 warranty, support, indemnity, or liability obligation is offered by You
214 alone, and You hereby agree to indemnify every Contributor for any
215 liability incurred by such Contributor as a result of warranty, support,
216 indemnity or liability terms You offer. You may include additional
217 disclaimers of warranty and limitations of liability specific to any
218 jurisdiction.
219
2204. Inability to Comply Due to Statute or Regulation
221
222 If it is impossible for You to comply with any of the terms of this License
223 with respect to some or all of the Covered Software due to statute, judicial
224 order, or regulation then You must: (a) comply with the terms of this License
225 to the maximum extent possible; and (b) describe the limitations and the code
226 they affect. Such description must be placed in a text file included with all
227 distributions of the Covered Software under this License. Except to the
228 extent prohibited by statute or regulation, such description must be
229 sufficiently detailed for a recipient of ordinary skill to be able to
230 understand it.
231
2325. Termination
233
2345.1. The rights granted under this License will terminate automatically if You
235 fail to comply with any of its terms. However, if You become compliant,
236 then the rights granted under this License from a particular Contributor
237 are reinstated (a) provisionally, unless and until such Contributor
238 explicitly and finally terminates Your grants, and (b) on an ongoing basis,
239 if such Contributor fails to notify You of the non-compliance by some
240 reasonable means prior to 60 days after You have come back into compliance.
241 Moreover, Your grants from a particular Contributor are reinstated on an
242 ongoing basis if such Contributor notifies You of the non-compliance by
243 some reasonable means, this is the first time You have received notice of
244 non-compliance with this License from such Contributor, and You become
245 compliant prior to 30 days after Your receipt of the notice.
246
2475.2. If You initiate litigation against any entity by asserting a patent
248 infringement claim (excluding declaratory judgment actions, counter-claims,
249 and cross-claims) alleging that a Contributor Version directly or
250 indirectly infringes any patent, then the rights granted to You by any and
251 all Contributors for the Covered Software under Section 2.1 of this License
252 shall terminate.
253
2545.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
255 license agreements (excluding distributors and resellers) which have been
256 validly granted by You or Your distributors under this License prior to
257 termination shall survive termination.
258
2596. Disclaimer of Warranty
260
261 Covered Software is provided under this License on an “as is” basis, without
262 warranty of any kind, either expressed, implied, or statutory, including,
263 without limitation, warranties that the Covered Software is free of defects,
264 merchantable, fit for a particular purpose or non-infringing. The entire
265 risk as to the quality and performance of the Covered Software is with You.
266 Should any Covered Software prove defective in any respect, You (not any
267 Contributor) assume the cost of any necessary servicing, repair, or
268 correction. This disclaimer of warranty constitutes an essential part of this
269 License. No use of any Covered Software is authorized under this License
270 except under this disclaimer.
271
2727. Limitation of Liability
273
274 Under no circumstances and under no legal theory, whether tort (including
275 negligence), contract, or otherwise, shall any Contributor, or anyone who
276 distributes Covered Software as permitted above, be liable to You for any
277 direct, indirect, special, incidental, or consequential damages of any
278 character including, without limitation, damages for lost profits, loss of
279 goodwill, work stoppage, computer failure or malfunction, or any and all
280 other commercial damages or losses, even if such party shall have been
281 informed of the possibility of such damages. This limitation of liability
282 shall not apply to liability for death or personal injury resulting from such
283 party’s negligence to the extent applicable law prohibits such limitation.
284 Some jurisdictions do not allow the exclusion or limitation of incidental or
285 consequential damages, so this exclusion and limitation may not apply to You.
286
2878. Litigation
288
289 Any litigation relating to this License may be brought only in the courts of
290 a jurisdiction where the defendant maintains its principal place of business
291 and such litigation shall be governed by laws of that jurisdiction, without
292 reference to its conflict-of-law provisions. Nothing in this Section shall
293 prevent a party’s ability to bring cross-claims or counter-claims.
294
2959. Miscellaneous
296
297 This License represents the complete agreement concerning the subject matter
298 hereof. If any provision of this License is held to be unenforceable, such
299 provision shall be reformed only to the extent necessary to make it
300 enforceable. Any law or regulation which provides that the language of a
301 contract shall be construed against the drafter shall not be used to construe
302 this License against a Contributor.
303
304
30510. Versions of the License
306
30710.1. New Versions
308
309 Mozilla Foundation is the license steward. Except as provided in Section
310 10.3, no one other than the license steward has the right to modify or
311 publish new versions of this License. Each version will be given a
312 distinguishing version number.
313
31410.2. Effect of New Versions
315
316 You may distribute the Covered Software under the terms of the version of
317 the License under which You originally received the Covered Software, or
318 under the terms of any subsequent version published by the license
319 steward.
320
32110.3. Modified Versions
322
323 If you create software not governed by this License, and you want to
324 create a new license for such software, you may create and use a modified
325 version of this License if you rename the license and remove any
326 references to the name of the license steward (except to note that such
327 modified license differs from this License).
328
32910.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
330 If You choose to distribute Source Code Form that is Incompatible With
331 Secondary Licenses under the terms of this version of the License, the
332 notice described in Exhibit B of this License must be attached.
333
334Exhibit A - Source Code Form License Notice
335
336 This Source Code Form is subject to the
337 terms of the Mozilla Public License, v.
338 2.0. If a copy of the MPL was not
339 distributed with this file, You can
340 obtain one at
341 http://mozilla.org/MPL/2.0/.
342
343If it is not possible or desirable to put the notice in a particular file, then
344You may include the notice in a location (such as a LICENSE file in a relevant
345directory) where a recipient would be likely to look for such a notice.
346
347You may add additional accurate notices of copyright ownership.
348
349Exhibit B - “Incompatible With Secondary Licenses” Notice
350
351 This Source Code Form is “Incompatible
352 With Secondary Licenses”, as defined by
353 the Mozilla Public License, v. 2.0.
diff --git a/vendor/github.com/hashicorp/hil/README.md b/vendor/github.com/hashicorp/hil/README.md
new file mode 100644
index 0000000..186ed25
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/README.md
@@ -0,0 +1,102 @@
1# HIL
2
3[![GoDoc](https://godoc.org/github.com/hashicorp/hil?status.png)](https://godoc.org/github.com/hashicorp/hil) [![Build Status](https://travis-ci.org/hashicorp/hil.svg?branch=master)](https://travis-ci.org/hashicorp/hil)
4
5HIL (HashiCorp Interpolation Language) is a lightweight embedded language used
6primarily for configuration interpolation. The goal of HIL is to make a simple
7language for interpolations in the various configurations of HashiCorp tools.
8
9HIL is built to interpolate any string, but is in use by HashiCorp primarily
10with [HCL](https://github.com/hashicorp/hcl). HCL is _not required_ in any
11way for use with HIL.
12
13HIL isn't meant to be a general purpose language. It was built for basic
14configuration interpolations. Therefore, you can't currently write functions,
15have conditionals, set intermediary variables, etc. within HIL itself. It is
16possible some of these may be added later but the right use case must exist.
17
18## Why?
19
20Many of our tools have support for something similar to templates, but
21within the configuration itself. The most prominent requirement was in
22[Terraform](https://github.com/hashicorp/terraform) where we wanted the
23configuration to be able to reference values from elsewhere in the
24configuration. Example:
25
26 foo = "hi ${var.world}"
27
28We originally used a full templating language for this, but found it
29was too heavy weight. Additionally, many full languages required bindings
30to C (and thus the usage of cgo) which we try to avoid to make cross-compilation
31easier. We then moved to very basic regular expression based
32string replacement, but found the need for basic arithmetic and function
33calls resulting in overly complex regular expressions.
34
35Ultimately, we wrote our own mini-language within Terraform itself. As
36we built other projects such as [Nomad](https://nomadproject.io) and
37[Otto](https://ottoproject.io), the need for basic interpolations arose
38again.
39
40Thus HIL was born. It is extracted from Terraform, cleaned up, and
41better tested for general purpose use.
42
43## Syntax
44
45For a complete grammar, please see the parser itself. A high-level overview
46of the syntax and grammer is listed here.
47
48Code begins within `${` and `}`. Outside of this, text is treated
49literally. For example, `foo` is a valid HIL program that is just the
50string "foo", but `foo ${bar}` is an HIL program that is the string "foo "
51concatened with the value of `bar`. For the remainder of the syntax
52docs, we'll assume you're within `${}`.
53
54 * Identifiers are any text in the format of `[a-zA-Z0-9-.]`. Example
55 identifiers: `foo`, `var.foo`, `foo-bar`.
56
57 * Strings are double quoted and can contain any UTF-8 characters.
58 Example: `"Hello, World"`
59
60 * Numbers are assumed to be base 10. If you prefix a number with 0x,
61 it is treated as a hexadecimal. If it is prefixed with 0, it is
62 treated as an octal. Numbers can be in scientific notation: "1e10".
63
64 * Unary `-` can be used for negative numbers. Example: `-10` or `-0.2`
65
66 * Boolean values: `true`, `false`
67
68 * The following arithmetic operations are allowed: +, -, *, /, %.
69
70 * Function calls are in the form of `name(arg1, arg2, ...)`. Example:
71 `add(1, 5)`. Arguments can be any valid HIL expression, example:
72 `add(1, var.foo)` or even nested function calls:
73 `add(1, get("some value"))`.
74
75 * Within strings, further interpolations can be opened with `${}`.
76 Example: `"Hello ${nested}"`. A full example including the
77 original `${}` (remember this list assumes were inside of one
78 already) could be: `foo ${func("hello ${var.foo}")}`.
79
80## Language Changes
81
82We've used this mini-language in Terraform for years. For backwards compatibility
83reasons, we're unlikely to make an incompatible change to the language but
84we're not currently making that promise, either.
85
86The internal API of this project may very well change as we evolve it
87to work with more of our projects. We recommend using some sort of dependency
88management solution with this package.
89
90## Future Changes
91
92The following changes are already planned to be made at some point:
93
94 * Richer types: lists, maps, etc.
95
96 * Convert to a more standard Go parser structure similar to HCL. This
97 will improve our error messaging as well as allow us to have automatic
98 formatting.
99
100 * Allow interpolations to result in more types than just a string. While
101 within the interpolation basic types are honored, the result is always
102 a string.
diff --git a/vendor/github.com/hashicorp/hil/appveyor.yml b/vendor/github.com/hashicorp/hil/appveyor.yml
new file mode 100644
index 0000000..feaf7a3
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/appveyor.yml
@@ -0,0 +1,18 @@
1version: "build-{branch}-{build}"
2image: Visual Studio 2015
3clone_folder: c:\gopath\src\github.com\hashicorp\hil
4environment:
5 GOPATH: c:\gopath
6init:
7 - git config --global core.autocrlf true
8install:
9- cmd: >-
10 echo %Path%
11
12 go version
13
14 go env
15
16 go get -d -v -t ./...
17build_script:
18- cmd: go test -v ./...
diff --git a/vendor/github.com/hashicorp/hil/ast/arithmetic.go b/vendor/github.com/hashicorp/hil/ast/arithmetic.go
new file mode 100644
index 0000000..94dc24f
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/arithmetic.go
@@ -0,0 +1,43 @@
1package ast
2
3import (
4 "bytes"
5 "fmt"
6)
7
8// Arithmetic represents a node where the result is arithmetic of
9// two or more operands in the order given.
10type Arithmetic struct {
11 Op ArithmeticOp
12 Exprs []Node
13 Posx Pos
14}
15
16func (n *Arithmetic) Accept(v Visitor) Node {
17 for i, expr := range n.Exprs {
18 n.Exprs[i] = expr.Accept(v)
19 }
20
21 return v(n)
22}
23
24func (n *Arithmetic) Pos() Pos {
25 return n.Posx
26}
27
28func (n *Arithmetic) GoString() string {
29 return fmt.Sprintf("*%#v", *n)
30}
31
32func (n *Arithmetic) String() string {
33 var b bytes.Buffer
34 for _, expr := range n.Exprs {
35 b.WriteString(fmt.Sprintf("%s", expr))
36 }
37
38 return b.String()
39}
40
41func (n *Arithmetic) Type(Scope) (Type, error) {
42 return TypeInt, nil
43}
diff --git a/vendor/github.com/hashicorp/hil/ast/arithmetic_op.go b/vendor/github.com/hashicorp/hil/ast/arithmetic_op.go
new file mode 100644
index 0000000..18880c6
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/arithmetic_op.go
@@ -0,0 +1,24 @@
1package ast
2
3// ArithmeticOp is the operation to use for the math.
4type ArithmeticOp int
5
6const (
7 ArithmeticOpInvalid ArithmeticOp = 0
8
9 ArithmeticOpAdd ArithmeticOp = iota
10 ArithmeticOpSub
11 ArithmeticOpMul
12 ArithmeticOpDiv
13 ArithmeticOpMod
14
15 ArithmeticOpLogicalAnd
16 ArithmeticOpLogicalOr
17
18 ArithmeticOpEqual
19 ArithmeticOpNotEqual
20 ArithmeticOpLessThan
21 ArithmeticOpLessThanOrEqual
22 ArithmeticOpGreaterThan
23 ArithmeticOpGreaterThanOrEqual
24)
diff --git a/vendor/github.com/hashicorp/hil/ast/ast.go b/vendor/github.com/hashicorp/hil/ast/ast.go
new file mode 100644
index 0000000..c6350f8
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/ast.go
@@ -0,0 +1,99 @@
1package ast
2
3import (
4 "fmt"
5)
6
7// Node is the interface that all AST nodes must implement.
8type Node interface {
9 // Accept is called to dispatch to the visitors. It must return the
10 // resulting Node (which might be different in an AST transform).
11 Accept(Visitor) Node
12
13 // Pos returns the position of this node in some source.
14 Pos() Pos
15
16 // Type returns the type of this node for the given context.
17 Type(Scope) (Type, error)
18}
19
20// Pos is the starting position of an AST node
21type Pos struct {
22 Column, Line int // Column/Line number, starting at 1
23 Filename string // Optional source filename, if known
24}
25
26func (p Pos) String() string {
27 if p.Filename == "" {
28 return fmt.Sprintf("%d:%d", p.Line, p.Column)
29 } else {
30 return fmt.Sprintf("%s:%d:%d", p.Filename, p.Line, p.Column)
31 }
32}
33
34// InitPos is an initiaial position value. This should be used as
35// the starting position (presets the column and line to 1).
36var InitPos = Pos{Column: 1, Line: 1}
37
38// Visitors are just implementations of this function.
39//
40// The function must return the Node to replace this node with. "nil" is
41// _not_ a valid return value. If there is no replacement, the original node
42// should be returned. We build this replacement directly into the visitor
43// pattern since AST transformations are a common and useful tool and
44// building it into the AST itself makes it required for future Node
45// implementations and very easy to do.
46//
47// Note that this isn't a true implementation of the visitor pattern, which
48// generally requires proper type dispatch on the function. However,
49// implementing this basic visitor pattern style is still very useful even
50// if you have to type switch.
51type Visitor func(Node) Node
52
53//go:generate stringer -type=Type
54
55// Type is the type of any value.
56type Type uint32
57
58const (
59 TypeInvalid Type = 0
60 TypeAny Type = 1 << iota
61 TypeBool
62 TypeString
63 TypeInt
64 TypeFloat
65 TypeList
66 TypeMap
67
68 // This is a special type used by Terraform to mark "unknown" values.
69 // It is impossible for this type to be introduced into your HIL programs
70 // unless you explicitly set a variable to this value. In that case,
71 // any operation including the variable will return "TypeUnknown" as the
72 // type.
73 TypeUnknown
74)
75
76func (t Type) Printable() string {
77 switch t {
78 case TypeInvalid:
79 return "invalid type"
80 case TypeAny:
81 return "any type"
82 case TypeBool:
83 return "type bool"
84 case TypeString:
85 return "type string"
86 case TypeInt:
87 return "type int"
88 case TypeFloat:
89 return "type float"
90 case TypeList:
91 return "type list"
92 case TypeMap:
93 return "type map"
94 case TypeUnknown:
95 return "type unknown"
96 default:
97 return "unknown type"
98 }
99}
diff --git a/vendor/github.com/hashicorp/hil/ast/call.go b/vendor/github.com/hashicorp/hil/ast/call.go
new file mode 100644
index 0000000..0557011
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/call.go
@@ -0,0 +1,47 @@
1package ast
2
3import (
4 "fmt"
5 "strings"
6)
7
8// Call represents a function call.
9type Call struct {
10 Func string
11 Args []Node
12 Posx Pos
13}
14
15func (n *Call) Accept(v Visitor) Node {
16 for i, a := range n.Args {
17 n.Args[i] = a.Accept(v)
18 }
19
20 return v(n)
21}
22
23func (n *Call) Pos() Pos {
24 return n.Posx
25}
26
27func (n *Call) String() string {
28 args := make([]string, len(n.Args))
29 for i, arg := range n.Args {
30 args[i] = fmt.Sprintf("%s", arg)
31 }
32
33 return fmt.Sprintf("Call(%s, %s)", n.Func, strings.Join(args, ", "))
34}
35
36func (n *Call) Type(s Scope) (Type, error) {
37 f, ok := s.LookupFunc(n.Func)
38 if !ok {
39 return TypeInvalid, fmt.Errorf("unknown function: %s", n.Func)
40 }
41
42 return f.ReturnType, nil
43}
44
45func (n *Call) GoString() string {
46 return fmt.Sprintf("*%#v", *n)
47}
diff --git a/vendor/github.com/hashicorp/hil/ast/conditional.go b/vendor/github.com/hashicorp/hil/ast/conditional.go
new file mode 100644
index 0000000..be48f89
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/conditional.go
@@ -0,0 +1,36 @@
1package ast
2
3import (
4 "fmt"
5)
6
7type Conditional struct {
8 CondExpr Node
9 TrueExpr Node
10 FalseExpr Node
11 Posx Pos
12}
13
14// Accept passes the given visitor to the child nodes in this order:
15// CondExpr, TrueExpr, FalseExpr. It then finally passes itself to the visitor.
16func (n *Conditional) Accept(v Visitor) Node {
17 n.CondExpr = n.CondExpr.Accept(v)
18 n.TrueExpr = n.TrueExpr.Accept(v)
19 n.FalseExpr = n.FalseExpr.Accept(v)
20
21 return v(n)
22}
23
24func (n *Conditional) Pos() Pos {
25 return n.Posx
26}
27
28func (n *Conditional) Type(Scope) (Type, error) {
29 // This is not actually a useful value; the type checker ignores
30 // this function when analyzing conditionals, just as with Arithmetic.
31 return TypeInt, nil
32}
33
34func (n *Conditional) GoString() string {
35 return fmt.Sprintf("*%#v", *n)
36}
diff --git a/vendor/github.com/hashicorp/hil/ast/index.go b/vendor/github.com/hashicorp/hil/ast/index.go
new file mode 100644
index 0000000..860c25f
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/index.go
@@ -0,0 +1,76 @@
1package ast
2
3import (
4 "fmt"
5 "strings"
6)
7
8// Index represents an indexing operation into another data structure
9type Index struct {
10 Target Node
11 Key Node
12 Posx Pos
13}
14
15func (n *Index) Accept(v Visitor) Node {
16 n.Target = n.Target.Accept(v)
17 n.Key = n.Key.Accept(v)
18 return v(n)
19}
20
21func (n *Index) Pos() Pos {
22 return n.Posx
23}
24
25func (n *Index) String() string {
26 return fmt.Sprintf("Index(%s, %s)", n.Target, n.Key)
27}
28
29func (n *Index) Type(s Scope) (Type, error) {
30 variableAccess, ok := n.Target.(*VariableAccess)
31 if !ok {
32 return TypeInvalid, fmt.Errorf("target is not a variable")
33 }
34
35 variable, ok := s.LookupVar(variableAccess.Name)
36 if !ok {
37 return TypeInvalid, fmt.Errorf("unknown variable accessed: %s", variableAccess.Name)
38 }
39
40 switch variable.Type {
41 case TypeList:
42 return n.typeList(variable, variableAccess.Name)
43 case TypeMap:
44 return n.typeMap(variable, variableAccess.Name)
45 default:
46 return TypeInvalid, fmt.Errorf("invalid index operation into non-indexable type: %s", variable.Type)
47 }
48}
49
50func (n *Index) typeList(variable Variable, variableName string) (Type, error) {
51 // We assume type checking has already determined that this is a list
52 list := variable.Value.([]Variable)
53
54 return VariableListElementTypesAreHomogenous(variableName, list)
55}
56
57func (n *Index) typeMap(variable Variable, variableName string) (Type, error) {
58 // We assume type checking has already determined that this is a map
59 vmap := variable.Value.(map[string]Variable)
60
61 return VariableMapValueTypesAreHomogenous(variableName, vmap)
62}
63
64func reportTypes(typesFound map[Type]struct{}) string {
65 stringTypes := make([]string, len(typesFound))
66 i := 0
67 for k, _ := range typesFound {
68 stringTypes[0] = k.String()
69 i++
70 }
71 return strings.Join(stringTypes, ", ")
72}
73
74func (n *Index) GoString() string {
75 return fmt.Sprintf("*%#v", *n)
76}
diff --git a/vendor/github.com/hashicorp/hil/ast/literal.go b/vendor/github.com/hashicorp/hil/ast/literal.go
new file mode 100644
index 0000000..da6014f
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/literal.go
@@ -0,0 +1,88 @@
1package ast
2
3import (
4 "fmt"
5 "reflect"
6)
7
8// LiteralNode represents a single literal value, such as "foo" or
9// 42 or 3.14159. Based on the Type, the Value can be safely cast.
10type LiteralNode struct {
11 Value interface{}
12 Typex Type
13 Posx Pos
14}
15
16// NewLiteralNode returns a new literal node representing the given
17// literal Go value, which must correspond to one of the primitive types
18// supported by HIL. Lists and maps cannot currently be constructed via
19// this function.
20//
21// If an inappropriately-typed value is provided, this function will
22// return an error. The main intended use of this function is to produce
23// "synthetic" literals from constants in code, where the value type is
24// well known at compile time. To easily store these in global variables,
25// see also MustNewLiteralNode.
26func NewLiteralNode(value interface{}, pos Pos) (*LiteralNode, error) {
27 goType := reflect.TypeOf(value)
28 var hilType Type
29
30 switch goType.Kind() {
31 case reflect.Bool:
32 hilType = TypeBool
33 case reflect.Int:
34 hilType = TypeInt
35 case reflect.Float64:
36 hilType = TypeFloat
37 case reflect.String:
38 hilType = TypeString
39 default:
40 return nil, fmt.Errorf("unsupported literal node type: %T", value)
41 }
42
43 return &LiteralNode{
44 Value: value,
45 Typex: hilType,
46 Posx: pos,
47 }, nil
48}
49
50// MustNewLiteralNode wraps NewLiteralNode and panics if an error is
51// returned, thus allowing valid literal nodes to be easily assigned to
52// global variables.
53func MustNewLiteralNode(value interface{}, pos Pos) *LiteralNode {
54 node, err := NewLiteralNode(value, pos)
55 if err != nil {
56 panic(err)
57 }
58 return node
59}
60
61func (n *LiteralNode) Accept(v Visitor) Node {
62 return v(n)
63}
64
65func (n *LiteralNode) Pos() Pos {
66 return n.Posx
67}
68
69func (n *LiteralNode) GoString() string {
70 return fmt.Sprintf("*%#v", *n)
71}
72
73func (n *LiteralNode) String() string {
74 return fmt.Sprintf("Literal(%s, %v)", n.Typex, n.Value)
75}
76
77func (n *LiteralNode) Type(Scope) (Type, error) {
78 return n.Typex, nil
79}
80
81// IsUnknown returns true either if the node's value is itself unknown
82// of if it is a collection containing any unknown elements, deeply.
83func (n *LiteralNode) IsUnknown() bool {
84 return IsUnknown(Variable{
85 Type: n.Typex,
86 Value: n.Value,
87 })
88}
diff --git a/vendor/github.com/hashicorp/hil/ast/output.go b/vendor/github.com/hashicorp/hil/ast/output.go
new file mode 100644
index 0000000..1e27f97
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/output.go
@@ -0,0 +1,78 @@
1package ast
2
3import (
4 "bytes"
5 "fmt"
6)
7
8// Output represents the root node of all interpolation evaluations. If the
9// output only has one expression which is either a TypeList or TypeMap, the
10// Output can be type-asserted to []interface{} or map[string]interface{}
11// respectively. Otherwise the Output evaluates as a string, and concatenates
12// the evaluation of each expression.
13type Output struct {
14 Exprs []Node
15 Posx Pos
16}
17
18func (n *Output) Accept(v Visitor) Node {
19 for i, expr := range n.Exprs {
20 n.Exprs[i] = expr.Accept(v)
21 }
22
23 return v(n)
24}
25
26func (n *Output) Pos() Pos {
27 return n.Posx
28}
29
30func (n *Output) GoString() string {
31 return fmt.Sprintf("*%#v", *n)
32}
33
34func (n *Output) String() string {
35 var b bytes.Buffer
36 for _, expr := range n.Exprs {
37 b.WriteString(fmt.Sprintf("%s", expr))
38 }
39
40 return b.String()
41}
42
43func (n *Output) Type(s Scope) (Type, error) {
44 // Special case no expressions for backward compatibility
45 if len(n.Exprs) == 0 {
46 return TypeString, nil
47 }
48
49 // Special case a single expression of types list or map
50 if len(n.Exprs) == 1 {
51 exprType, err := n.Exprs[0].Type(s)
52 if err != nil {
53 return TypeInvalid, err
54 }
55 switch exprType {
56 case TypeList:
57 return TypeList, nil
58 case TypeMap:
59 return TypeMap, nil
60 }
61 }
62
63 // Otherwise ensure all our expressions are strings
64 for index, expr := range n.Exprs {
65 exprType, err := expr.Type(s)
66 if err != nil {
67 return TypeInvalid, err
68 }
69 // We only look for things we know we can't coerce with an implicit conversion func
70 if exprType == TypeList || exprType == TypeMap {
71 return TypeInvalid, fmt.Errorf(
72 "multi-expression HIL outputs may only have string inputs: %d is type %s",
73 index, exprType)
74 }
75 }
76
77 return TypeString, nil
78}
diff --git a/vendor/github.com/hashicorp/hil/ast/scope.go b/vendor/github.com/hashicorp/hil/ast/scope.go
new file mode 100644
index 0000000..7a975d9
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/scope.go
@@ -0,0 +1,90 @@
1package ast
2
3import (
4 "fmt"
5 "reflect"
6)
7
8// Scope is the interface used to look up variables and functions while
9// evaluating. How these functions/variables are defined are up to the caller.
10type Scope interface {
11 LookupFunc(string) (Function, bool)
12 LookupVar(string) (Variable, bool)
13}
14
15// Variable is a variable value for execution given as input to the engine.
16// It records the value of a variables along with their type.
17type Variable struct {
18 Value interface{}
19 Type Type
20}
21
22// NewVariable creates a new Variable for the given value. This will
23// attempt to infer the correct type. If it can't, an error will be returned.
24func NewVariable(v interface{}) (result Variable, err error) {
25 switch v := reflect.ValueOf(v); v.Kind() {
26 case reflect.String:
27 result.Type = TypeString
28 default:
29 err = fmt.Errorf("Unknown type: %s", v.Kind())
30 }
31
32 result.Value = v
33 return
34}
35
36// String implements Stringer on Variable, displaying the type and value
37// of the Variable.
38func (v Variable) String() string {
39 return fmt.Sprintf("{Variable (%s): %+v}", v.Type, v.Value)
40}
41
42// Function defines a function that can be executed by the engine.
43// The type checker will validate that the proper types will be called
44// to the callback.
45type Function struct {
46 // ArgTypes is the list of types in argument order. These are the
47 // required arguments.
48 //
49 // ReturnType is the type of the returned value. The Callback MUST
50 // return this type.
51 ArgTypes []Type
52 ReturnType Type
53
54 // Variadic, if true, says that this function is variadic, meaning
55 // it takes a variable number of arguments. In this case, the
56 // VariadicType must be set.
57 Variadic bool
58 VariadicType Type
59
60 // Callback is the function called for a function. The argument
61 // types are guaranteed to match the spec above by the type checker.
62 // The length of the args is strictly == len(ArgTypes) unless Varidiac
63 // is true, in which case its >= len(ArgTypes).
64 Callback func([]interface{}) (interface{}, error)
65}
66
67// BasicScope is a simple scope that looks up variables and functions
68// using a map.
69type BasicScope struct {
70 FuncMap map[string]Function
71 VarMap map[string]Variable
72}
73
74func (s *BasicScope) LookupFunc(n string) (Function, bool) {
75 if s == nil {
76 return Function{}, false
77 }
78
79 v, ok := s.FuncMap[n]
80 return v, ok
81}
82
83func (s *BasicScope) LookupVar(n string) (Variable, bool) {
84 if s == nil {
85 return Variable{}, false
86 }
87
88 v, ok := s.VarMap[n]
89 return v, ok
90}
diff --git a/vendor/github.com/hashicorp/hil/ast/stack.go b/vendor/github.com/hashicorp/hil/ast/stack.go
new file mode 100644
index 0000000..bd2bc15
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/stack.go
@@ -0,0 +1,25 @@
1package ast
2
3// Stack is a stack of Node.
4type Stack struct {
5 stack []Node
6}
7
8func (s *Stack) Len() int {
9 return len(s.stack)
10}
11
12func (s *Stack) Push(n Node) {
13 s.stack = append(s.stack, n)
14}
15
16func (s *Stack) Pop() Node {
17 x := s.stack[len(s.stack)-1]
18 s.stack[len(s.stack)-1] = nil
19 s.stack = s.stack[:len(s.stack)-1]
20 return x
21}
22
23func (s *Stack) Reset() {
24 s.stack = nil
25}
diff --git a/vendor/github.com/hashicorp/hil/ast/type_string.go b/vendor/github.com/hashicorp/hil/ast/type_string.go
new file mode 100644
index 0000000..1f51a98
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/type_string.go
@@ -0,0 +1,54 @@
1// Code generated by "stringer -type=Type"; DO NOT EDIT
2
3package ast
4
5import "fmt"
6
7const (
8 _Type_name_0 = "TypeInvalid"
9 _Type_name_1 = "TypeAny"
10 _Type_name_2 = "TypeBool"
11 _Type_name_3 = "TypeString"
12 _Type_name_4 = "TypeInt"
13 _Type_name_5 = "TypeFloat"
14 _Type_name_6 = "TypeList"
15 _Type_name_7 = "TypeMap"
16 _Type_name_8 = "TypeUnknown"
17)
18
19var (
20 _Type_index_0 = [...]uint8{0, 11}
21 _Type_index_1 = [...]uint8{0, 7}
22 _Type_index_2 = [...]uint8{0, 8}
23 _Type_index_3 = [...]uint8{0, 10}
24 _Type_index_4 = [...]uint8{0, 7}
25 _Type_index_5 = [...]uint8{0, 9}
26 _Type_index_6 = [...]uint8{0, 8}
27 _Type_index_7 = [...]uint8{0, 7}
28 _Type_index_8 = [...]uint8{0, 11}
29)
30
31func (i Type) String() string {
32 switch {
33 case i == 0:
34 return _Type_name_0
35 case i == 2:
36 return _Type_name_1
37 case i == 4:
38 return _Type_name_2
39 case i == 8:
40 return _Type_name_3
41 case i == 16:
42 return _Type_name_4
43 case i == 32:
44 return _Type_name_5
45 case i == 64:
46 return _Type_name_6
47 case i == 128:
48 return _Type_name_7
49 case i == 256:
50 return _Type_name_8
51 default:
52 return fmt.Sprintf("Type(%d)", i)
53 }
54}
diff --git a/vendor/github.com/hashicorp/hil/ast/unknown.go b/vendor/github.com/hashicorp/hil/ast/unknown.go
new file mode 100644
index 0000000..d6ddaec
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/unknown.go
@@ -0,0 +1,30 @@
1package ast
2
3// IsUnknown reports whether a variable is unknown or contains any value
4// that is unknown. This will recurse into lists and maps and so on.
5func IsUnknown(v Variable) bool {
6 // If it is unknown itself, return true
7 if v.Type == TypeUnknown {
8 return true
9 }
10
11 // If it is a container type, check the values
12 switch v.Type {
13 case TypeList:
14 for _, el := range v.Value.([]Variable) {
15 if IsUnknown(el) {
16 return true
17 }
18 }
19 case TypeMap:
20 for _, el := range v.Value.(map[string]Variable) {
21 if IsUnknown(el) {
22 return true
23 }
24 }
25 default:
26 }
27
28 // Not a container type or survive the above checks
29 return false
30}
diff --git a/vendor/github.com/hashicorp/hil/ast/variable_access.go b/vendor/github.com/hashicorp/hil/ast/variable_access.go
new file mode 100644
index 0000000..4c1362d
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/variable_access.go
@@ -0,0 +1,36 @@
1package ast
2
3import (
4 "fmt"
5)
6
7// VariableAccess represents a variable access.
8type VariableAccess struct {
9 Name string
10 Posx Pos
11}
12
13func (n *VariableAccess) Accept(v Visitor) Node {
14 return v(n)
15}
16
17func (n *VariableAccess) Pos() Pos {
18 return n.Posx
19}
20
21func (n *VariableAccess) GoString() string {
22 return fmt.Sprintf("*%#v", *n)
23}
24
25func (n *VariableAccess) String() string {
26 return fmt.Sprintf("Variable(%s)", n.Name)
27}
28
29func (n *VariableAccess) Type(s Scope) (Type, error) {
30 v, ok := s.LookupVar(n.Name)
31 if !ok {
32 return TypeInvalid, fmt.Errorf("unknown variable: %s", n.Name)
33 }
34
35 return v.Type, nil
36}
diff --git a/vendor/github.com/hashicorp/hil/ast/variables_helper.go b/vendor/github.com/hashicorp/hil/ast/variables_helper.go
new file mode 100644
index 0000000..06bd18d
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/ast/variables_helper.go
@@ -0,0 +1,63 @@
1package ast
2
3import "fmt"
4
5func VariableListElementTypesAreHomogenous(variableName string, list []Variable) (Type, error) {
6 if len(list) == 0 {
7 return TypeInvalid, fmt.Errorf("list %q does not have any elements so cannot determine type.", variableName)
8 }
9
10 elemType := TypeUnknown
11 for _, v := range list {
12 if v.Type == TypeUnknown {
13 continue
14 }
15
16 if elemType == TypeUnknown {
17 elemType = v.Type
18 continue
19 }
20
21 if v.Type != elemType {
22 return TypeInvalid, fmt.Errorf(
23 "list %q does not have homogenous types. found %s and then %s",
24 variableName,
25 elemType, v.Type,
26 )
27 }
28
29 elemType = v.Type
30 }
31
32 return elemType, nil
33}
34
35func VariableMapValueTypesAreHomogenous(variableName string, vmap map[string]Variable) (Type, error) {
36 if len(vmap) == 0 {
37 return TypeInvalid, fmt.Errorf("map %q does not have any elements so cannot determine type.", variableName)
38 }
39
40 elemType := TypeUnknown
41 for _, v := range vmap {
42 if v.Type == TypeUnknown {
43 continue
44 }
45
46 if elemType == TypeUnknown {
47 elemType = v.Type
48 continue
49 }
50
51 if v.Type != elemType {
52 return TypeInvalid, fmt.Errorf(
53 "map %q does not have homogenous types. found %s and then %s",
54 variableName,
55 elemType, v.Type,
56 )
57 }
58
59 elemType = v.Type
60 }
61
62 return elemType, nil
63}
diff --git a/vendor/github.com/hashicorp/hil/builtins.go b/vendor/github.com/hashicorp/hil/builtins.go
new file mode 100644
index 0000000..909c788
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/builtins.go
@@ -0,0 +1,331 @@
1package hil
2
3import (
4 "errors"
5 "strconv"
6
7 "github.com/hashicorp/hil/ast"
8)
9
10// NOTE: All builtins are tested in engine_test.go
11
12func registerBuiltins(scope *ast.BasicScope) *ast.BasicScope {
13 if scope == nil {
14 scope = new(ast.BasicScope)
15 }
16 if scope.FuncMap == nil {
17 scope.FuncMap = make(map[string]ast.Function)
18 }
19
20 // Implicit conversions
21 scope.FuncMap["__builtin_BoolToString"] = builtinBoolToString()
22 scope.FuncMap["__builtin_FloatToInt"] = builtinFloatToInt()
23 scope.FuncMap["__builtin_FloatToString"] = builtinFloatToString()
24 scope.FuncMap["__builtin_IntToFloat"] = builtinIntToFloat()
25 scope.FuncMap["__builtin_IntToString"] = builtinIntToString()
26 scope.FuncMap["__builtin_StringToInt"] = builtinStringToInt()
27 scope.FuncMap["__builtin_StringToFloat"] = builtinStringToFloat()
28 scope.FuncMap["__builtin_StringToBool"] = builtinStringToBool()
29
30 // Math operations
31 scope.FuncMap["__builtin_IntMath"] = builtinIntMath()
32 scope.FuncMap["__builtin_FloatMath"] = builtinFloatMath()
33 scope.FuncMap["__builtin_BoolCompare"] = builtinBoolCompare()
34 scope.FuncMap["__builtin_FloatCompare"] = builtinFloatCompare()
35 scope.FuncMap["__builtin_IntCompare"] = builtinIntCompare()
36 scope.FuncMap["__builtin_StringCompare"] = builtinStringCompare()
37 scope.FuncMap["__builtin_Logical"] = builtinLogical()
38 return scope
39}
40
41func builtinFloatMath() ast.Function {
42 return ast.Function{
43 ArgTypes: []ast.Type{ast.TypeInt},
44 Variadic: true,
45 VariadicType: ast.TypeFloat,
46 ReturnType: ast.TypeFloat,
47 Callback: func(args []interface{}) (interface{}, error) {
48 op := args[0].(ast.ArithmeticOp)
49 result := args[1].(float64)
50 for _, raw := range args[2:] {
51 arg := raw.(float64)
52 switch op {
53 case ast.ArithmeticOpAdd:
54 result += arg
55 case ast.ArithmeticOpSub:
56 result -= arg
57 case ast.ArithmeticOpMul:
58 result *= arg
59 case ast.ArithmeticOpDiv:
60 result /= arg
61 }
62 }
63
64 return result, nil
65 },
66 }
67}
68
69func builtinIntMath() ast.Function {
70 return ast.Function{
71 ArgTypes: []ast.Type{ast.TypeInt},
72 Variadic: true,
73 VariadicType: ast.TypeInt,
74 ReturnType: ast.TypeInt,
75 Callback: func(args []interface{}) (interface{}, error) {
76 op := args[0].(ast.ArithmeticOp)
77 result := args[1].(int)
78 for _, raw := range args[2:] {
79 arg := raw.(int)
80 switch op {
81 case ast.ArithmeticOpAdd:
82 result += arg
83 case ast.ArithmeticOpSub:
84 result -= arg
85 case ast.ArithmeticOpMul:
86 result *= arg
87 case ast.ArithmeticOpDiv:
88 if arg == 0 {
89 return nil, errors.New("divide by zero")
90 }
91
92 result /= arg
93 case ast.ArithmeticOpMod:
94 if arg == 0 {
95 return nil, errors.New("divide by zero")
96 }
97
98 result = result % arg
99 }
100 }
101
102 return result, nil
103 },
104 }
105}
106
107func builtinBoolCompare() ast.Function {
108 return ast.Function{
109 ArgTypes: []ast.Type{ast.TypeInt, ast.TypeBool, ast.TypeBool},
110 Variadic: false,
111 ReturnType: ast.TypeBool,
112 Callback: func(args []interface{}) (interface{}, error) {
113 op := args[0].(ast.ArithmeticOp)
114 lhs := args[1].(bool)
115 rhs := args[2].(bool)
116
117 switch op {
118 case ast.ArithmeticOpEqual:
119 return lhs == rhs, nil
120 case ast.ArithmeticOpNotEqual:
121 return lhs != rhs, nil
122 default:
123 return nil, errors.New("invalid comparison operation")
124 }
125 },
126 }
127}
128
129func builtinFloatCompare() ast.Function {
130 return ast.Function{
131 ArgTypes: []ast.Type{ast.TypeInt, ast.TypeFloat, ast.TypeFloat},
132 Variadic: false,
133 ReturnType: ast.TypeBool,
134 Callback: func(args []interface{}) (interface{}, error) {
135 op := args[0].(ast.ArithmeticOp)
136 lhs := args[1].(float64)
137 rhs := args[2].(float64)
138
139 switch op {
140 case ast.ArithmeticOpEqual:
141 return lhs == rhs, nil
142 case ast.ArithmeticOpNotEqual:
143 return lhs != rhs, nil
144 case ast.ArithmeticOpLessThan:
145 return lhs < rhs, nil
146 case ast.ArithmeticOpLessThanOrEqual:
147 return lhs <= rhs, nil
148 case ast.ArithmeticOpGreaterThan:
149 return lhs > rhs, nil
150 case ast.ArithmeticOpGreaterThanOrEqual:
151 return lhs >= rhs, nil
152 default:
153 return nil, errors.New("invalid comparison operation")
154 }
155 },
156 }
157}
158
159func builtinIntCompare() ast.Function {
160 return ast.Function{
161 ArgTypes: []ast.Type{ast.TypeInt, ast.TypeInt, ast.TypeInt},
162 Variadic: false,
163 ReturnType: ast.TypeBool,
164 Callback: func(args []interface{}) (interface{}, error) {
165 op := args[0].(ast.ArithmeticOp)
166 lhs := args[1].(int)
167 rhs := args[2].(int)
168
169 switch op {
170 case ast.ArithmeticOpEqual:
171 return lhs == rhs, nil
172 case ast.ArithmeticOpNotEqual:
173 return lhs != rhs, nil
174 case ast.ArithmeticOpLessThan:
175 return lhs < rhs, nil
176 case ast.ArithmeticOpLessThanOrEqual:
177 return lhs <= rhs, nil
178 case ast.ArithmeticOpGreaterThan:
179 return lhs > rhs, nil
180 case ast.ArithmeticOpGreaterThanOrEqual:
181 return lhs >= rhs, nil
182 default:
183 return nil, errors.New("invalid comparison operation")
184 }
185 },
186 }
187}
188
189func builtinStringCompare() ast.Function {
190 return ast.Function{
191 ArgTypes: []ast.Type{ast.TypeInt, ast.TypeString, ast.TypeString},
192 Variadic: false,
193 ReturnType: ast.TypeBool,
194 Callback: func(args []interface{}) (interface{}, error) {
195 op := args[0].(ast.ArithmeticOp)
196 lhs := args[1].(string)
197 rhs := args[2].(string)
198
199 switch op {
200 case ast.ArithmeticOpEqual:
201 return lhs == rhs, nil
202 case ast.ArithmeticOpNotEqual:
203 return lhs != rhs, nil
204 default:
205 return nil, errors.New("invalid comparison operation")
206 }
207 },
208 }
209}
210
211func builtinLogical() ast.Function {
212 return ast.Function{
213 ArgTypes: []ast.Type{ast.TypeInt},
214 Variadic: true,
215 VariadicType: ast.TypeBool,
216 ReturnType: ast.TypeBool,
217 Callback: func(args []interface{}) (interface{}, error) {
218 op := args[0].(ast.ArithmeticOp)
219 result := args[1].(bool)
220 for _, raw := range args[2:] {
221 arg := raw.(bool)
222 switch op {
223 case ast.ArithmeticOpLogicalOr:
224 result = result || arg
225 case ast.ArithmeticOpLogicalAnd:
226 result = result && arg
227 default:
228 return nil, errors.New("invalid logical operator")
229 }
230 }
231
232 return result, nil
233 },
234 }
235}
236
237func builtinFloatToInt() ast.Function {
238 return ast.Function{
239 ArgTypes: []ast.Type{ast.TypeFloat},
240 ReturnType: ast.TypeInt,
241 Callback: func(args []interface{}) (interface{}, error) {
242 return int(args[0].(float64)), nil
243 },
244 }
245}
246
247func builtinFloatToString() ast.Function {
248 return ast.Function{
249 ArgTypes: []ast.Type{ast.TypeFloat},
250 ReturnType: ast.TypeString,
251 Callback: func(args []interface{}) (interface{}, error) {
252 return strconv.FormatFloat(
253 args[0].(float64), 'g', -1, 64), nil
254 },
255 }
256}
257
258func builtinIntToFloat() ast.Function {
259 return ast.Function{
260 ArgTypes: []ast.Type{ast.TypeInt},
261 ReturnType: ast.TypeFloat,
262 Callback: func(args []interface{}) (interface{}, error) {
263 return float64(args[0].(int)), nil
264 },
265 }
266}
267
268func builtinIntToString() ast.Function {
269 return ast.Function{
270 ArgTypes: []ast.Type{ast.TypeInt},
271 ReturnType: ast.TypeString,
272 Callback: func(args []interface{}) (interface{}, error) {
273 return strconv.FormatInt(int64(args[0].(int)), 10), nil
274 },
275 }
276}
277
278func builtinStringToInt() ast.Function {
279 return ast.Function{
280 ArgTypes: []ast.Type{ast.TypeInt},
281 ReturnType: ast.TypeString,
282 Callback: func(args []interface{}) (interface{}, error) {
283 v, err := strconv.ParseInt(args[0].(string), 0, 0)
284 if err != nil {
285 return nil, err
286 }
287
288 return int(v), nil
289 },
290 }
291}
292
293func builtinStringToFloat() ast.Function {
294 return ast.Function{
295 ArgTypes: []ast.Type{ast.TypeString},
296 ReturnType: ast.TypeFloat,
297 Callback: func(args []interface{}) (interface{}, error) {
298 v, err := strconv.ParseFloat(args[0].(string), 64)
299 if err != nil {
300 return nil, err
301 }
302
303 return v, nil
304 },
305 }
306}
307
308func builtinBoolToString() ast.Function {
309 return ast.Function{
310 ArgTypes: []ast.Type{ast.TypeBool},
311 ReturnType: ast.TypeString,
312 Callback: func(args []interface{}) (interface{}, error) {
313 return strconv.FormatBool(args[0].(bool)), nil
314 },
315 }
316}
317
318func builtinStringToBool() ast.Function {
319 return ast.Function{
320 ArgTypes: []ast.Type{ast.TypeString},
321 ReturnType: ast.TypeBool,
322 Callback: func(args []interface{}) (interface{}, error) {
323 v, err := strconv.ParseBool(args[0].(string))
324 if err != nil {
325 return nil, err
326 }
327
328 return v, nil
329 },
330 }
331}
diff --git a/vendor/github.com/hashicorp/hil/check_identifier.go b/vendor/github.com/hashicorp/hil/check_identifier.go
new file mode 100644
index 0000000..474f505
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/check_identifier.go
@@ -0,0 +1,88 @@
1package hil
2
3import (
4 "fmt"
5 "sync"
6
7 "github.com/hashicorp/hil/ast"
8)
9
10// IdentifierCheck is a SemanticCheck that checks that all identifiers
11// resolve properly and that the right number of arguments are passed
12// to functions.
13type IdentifierCheck struct {
14 Scope ast.Scope
15
16 err error
17 lock sync.Mutex
18}
19
20func (c *IdentifierCheck) Visit(root ast.Node) error {
21 c.lock.Lock()
22 defer c.lock.Unlock()
23 defer c.reset()
24 root.Accept(c.visit)
25 return c.err
26}
27
28func (c *IdentifierCheck) visit(raw ast.Node) ast.Node {
29 if c.err != nil {
30 return raw
31 }
32
33 switch n := raw.(type) {
34 case *ast.Call:
35 c.visitCall(n)
36 case *ast.VariableAccess:
37 c.visitVariableAccess(n)
38 case *ast.Output:
39 // Ignore
40 case *ast.LiteralNode:
41 // Ignore
42 default:
43 // Ignore
44 }
45
46 // We never do replacement with this visitor
47 return raw
48}
49
50func (c *IdentifierCheck) visitCall(n *ast.Call) {
51 // Look up the function in the map
52 function, ok := c.Scope.LookupFunc(n.Func)
53 if !ok {
54 c.createErr(n, fmt.Sprintf("unknown function called: %s", n.Func))
55 return
56 }
57
58 // Break up the args into what is variadic and what is required
59 args := n.Args
60 if function.Variadic && len(args) > len(function.ArgTypes) {
61 args = n.Args[:len(function.ArgTypes)]
62 }
63
64 // Verify the number of arguments
65 if len(args) != len(function.ArgTypes) {
66 c.createErr(n, fmt.Sprintf(
67 "%s: expected %d arguments, got %d",
68 n.Func, len(function.ArgTypes), len(n.Args)))
69 return
70 }
71}
72
73func (c *IdentifierCheck) visitVariableAccess(n *ast.VariableAccess) {
74 // Look up the variable in the map
75 if _, ok := c.Scope.LookupVar(n.Name); !ok {
76 c.createErr(n, fmt.Sprintf(
77 "unknown variable accessed: %s", n.Name))
78 return
79 }
80}
81
82func (c *IdentifierCheck) createErr(n ast.Node, str string) {
83 c.err = fmt.Errorf("%s: %s", n.Pos(), str)
84}
85
86func (c *IdentifierCheck) reset() {
87 c.err = nil
88}
diff --git a/vendor/github.com/hashicorp/hil/check_types.go b/vendor/github.com/hashicorp/hil/check_types.go
new file mode 100644
index 0000000..f16da39
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/check_types.go
@@ -0,0 +1,668 @@
1package hil
2
3import (
4 "fmt"
5 "sync"
6
7 "github.com/hashicorp/hil/ast"
8)
9
10// TypeCheck implements ast.Visitor for type checking an AST tree.
11// It requires some configuration to look up the type of nodes.
12//
13// It also optionally will not type error and will insert an implicit
14// type conversions for specific types if specified by the Implicit
15// field. Note that this is kind of organizationally weird to put into
16// this structure but we'd rather do that than duplicate the type checking
17// logic multiple times.
18type TypeCheck struct {
19 Scope ast.Scope
20
21 // Implicit is a map of implicit type conversions that we can do,
22 // and that shouldn't error. The key of the first map is the from type,
23 // the key of the second map is the to type, and the final string
24 // value is the function to call (which must be registered in the Scope).
25 Implicit map[ast.Type]map[ast.Type]string
26
27 // Stack of types. This shouldn't be used directly except by implementations
28 // of TypeCheckNode.
29 Stack []ast.Type
30
31 err error
32 lock sync.Mutex
33}
34
35// TypeCheckNode is the interface that must be implemented by any
36// ast.Node that wants to support type-checking. If the type checker
37// encounters a node that doesn't implement this, it will error.
38type TypeCheckNode interface {
39 TypeCheck(*TypeCheck) (ast.Node, error)
40}
41
42func (v *TypeCheck) Visit(root ast.Node) error {
43 v.lock.Lock()
44 defer v.lock.Unlock()
45 defer v.reset()
46 root.Accept(v.visit)
47
48 // If the resulting type is unknown, then just let the whole thing go.
49 if v.err == errExitUnknown {
50 v.err = nil
51 }
52
53 return v.err
54}
55
56func (v *TypeCheck) visit(raw ast.Node) ast.Node {
57 if v.err != nil {
58 return raw
59 }
60
61 var result ast.Node
62 var err error
63 switch n := raw.(type) {
64 case *ast.Arithmetic:
65 tc := &typeCheckArithmetic{n}
66 result, err = tc.TypeCheck(v)
67 case *ast.Call:
68 tc := &typeCheckCall{n}
69 result, err = tc.TypeCheck(v)
70 case *ast.Conditional:
71 tc := &typeCheckConditional{n}
72 result, err = tc.TypeCheck(v)
73 case *ast.Index:
74 tc := &typeCheckIndex{n}
75 result, err = tc.TypeCheck(v)
76 case *ast.Output:
77 tc := &typeCheckOutput{n}
78 result, err = tc.TypeCheck(v)
79 case *ast.LiteralNode:
80 tc := &typeCheckLiteral{n}
81 result, err = tc.TypeCheck(v)
82 case *ast.VariableAccess:
83 tc := &typeCheckVariableAccess{n}
84 result, err = tc.TypeCheck(v)
85 default:
86 tc, ok := raw.(TypeCheckNode)
87 if !ok {
88 err = fmt.Errorf("unknown node for type check: %#v", raw)
89 break
90 }
91
92 result, err = tc.TypeCheck(v)
93 }
94
95 if err != nil {
96 pos := raw.Pos()
97 v.err = fmt.Errorf("At column %d, line %d: %s",
98 pos.Column, pos.Line, err)
99 }
100
101 return result
102}
103
104type typeCheckArithmetic struct {
105 n *ast.Arithmetic
106}
107
108func (tc *typeCheckArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) {
109 // The arguments are on the stack in reverse order, so pop them off.
110 exprs := make([]ast.Type, len(tc.n.Exprs))
111 for i, _ := range tc.n.Exprs {
112 exprs[len(tc.n.Exprs)-1-i] = v.StackPop()
113 }
114
115 // If any operand is unknown then our result is automatically unknown
116 for _, ty := range exprs {
117 if ty == ast.TypeUnknown {
118 v.StackPush(ast.TypeUnknown)
119 return tc.n, nil
120 }
121 }
122
123 switch tc.n.Op {
124 case ast.ArithmeticOpLogicalAnd, ast.ArithmeticOpLogicalOr:
125 return tc.checkLogical(v, exprs)
126 case ast.ArithmeticOpEqual, ast.ArithmeticOpNotEqual,
127 ast.ArithmeticOpLessThan, ast.ArithmeticOpGreaterThan,
128 ast.ArithmeticOpGreaterThanOrEqual, ast.ArithmeticOpLessThanOrEqual:
129 return tc.checkComparison(v, exprs)
130 default:
131 return tc.checkNumeric(v, exprs)
132 }
133
134}
135
136func (tc *typeCheckArithmetic) checkNumeric(v *TypeCheck, exprs []ast.Type) (ast.Node, error) {
137 // Determine the resulting type we want. We do this by going over
138 // every expression until we find one with a type we recognize.
139 // We do this because the first expr might be a string ("var.foo")
140 // and we need to know what to implicit to.
141 mathFunc := "__builtin_IntMath"
142 mathType := ast.TypeInt
143 for _, v := range exprs {
144 // We assume int math but if we find ANY float, the entire
145 // expression turns into floating point math.
146 if v == ast.TypeFloat {
147 mathFunc = "__builtin_FloatMath"
148 mathType = v
149 break
150 }
151 }
152
153 // Verify the args
154 for i, arg := range exprs {
155 if arg != mathType {
156 cn := v.ImplicitConversion(exprs[i], mathType, tc.n.Exprs[i])
157 if cn != nil {
158 tc.n.Exprs[i] = cn
159 continue
160 }
161
162 return nil, fmt.Errorf(
163 "operand %d should be %s, got %s",
164 i+1, mathType, arg)
165 }
166 }
167
168 // Modulo doesn't work for floats
169 if mathType == ast.TypeFloat && tc.n.Op == ast.ArithmeticOpMod {
170 return nil, fmt.Errorf("modulo cannot be used with floats")
171 }
172
173 // Return type
174 v.StackPush(mathType)
175
176 // Replace our node with a call to the proper function. This isn't
177 // type checked but we already verified types.
178 args := make([]ast.Node, len(tc.n.Exprs)+1)
179 args[0] = &ast.LiteralNode{
180 Value: tc.n.Op,
181 Typex: ast.TypeInt,
182 Posx: tc.n.Pos(),
183 }
184 copy(args[1:], tc.n.Exprs)
185 return &ast.Call{
186 Func: mathFunc,
187 Args: args,
188 Posx: tc.n.Pos(),
189 }, nil
190}
191
192func (tc *typeCheckArithmetic) checkComparison(v *TypeCheck, exprs []ast.Type) (ast.Node, error) {
193 if len(exprs) != 2 {
194 // This should never happen, because the parser never produces
195 // nodes that violate this.
196 return nil, fmt.Errorf(
197 "comparison operators must have exactly two operands",
198 )
199 }
200
201 // The first operand always dictates the type for a comparison.
202 compareFunc := ""
203 compareType := exprs[0]
204 switch compareType {
205 case ast.TypeBool:
206 compareFunc = "__builtin_BoolCompare"
207 case ast.TypeFloat:
208 compareFunc = "__builtin_FloatCompare"
209 case ast.TypeInt:
210 compareFunc = "__builtin_IntCompare"
211 case ast.TypeString:
212 compareFunc = "__builtin_StringCompare"
213 default:
214 return nil, fmt.Errorf(
215 "comparison operators apply only to bool, float, int, and string",
216 )
217 }
218
219 // For non-equality comparisons, we will do implicit conversions to
220 // integer types if possible. In this case, we need to go through and
221 // determine the type of comparison we're doing to enable the implicit
222 // conversion.
223 if tc.n.Op != ast.ArithmeticOpEqual && tc.n.Op != ast.ArithmeticOpNotEqual {
224 compareFunc = "__builtin_IntCompare"
225 compareType = ast.TypeInt
226 for _, expr := range exprs {
227 if expr == ast.TypeFloat {
228 compareFunc = "__builtin_FloatCompare"
229 compareType = ast.TypeFloat
230 break
231 }
232 }
233 }
234
235 // Verify (and possibly, convert) the args
236 for i, arg := range exprs {
237 if arg != compareType {
238 cn := v.ImplicitConversion(exprs[i], compareType, tc.n.Exprs[i])
239 if cn != nil {
240 tc.n.Exprs[i] = cn
241 continue
242 }
243
244 return nil, fmt.Errorf(
245 "operand %d should be %s, got %s",
246 i+1, compareType, arg,
247 )
248 }
249 }
250
251 // Only ints and floats can have the <, >, <= and >= operators applied
252 switch tc.n.Op {
253 case ast.ArithmeticOpEqual, ast.ArithmeticOpNotEqual:
254 // anything goes
255 default:
256 switch compareType {
257 case ast.TypeFloat, ast.TypeInt:
258 // fine
259 default:
260 return nil, fmt.Errorf(
261 "<, >, <= and >= may apply only to int and float values",
262 )
263 }
264 }
265
266 // Comparison operators always return bool
267 v.StackPush(ast.TypeBool)
268
269 // Replace our node with a call to the proper function. This isn't
270 // type checked but we already verified types.
271 args := make([]ast.Node, len(tc.n.Exprs)+1)
272 args[0] = &ast.LiteralNode{
273 Value: tc.n.Op,
274 Typex: ast.TypeInt,
275 Posx: tc.n.Pos(),
276 }
277 copy(args[1:], tc.n.Exprs)
278 return &ast.Call{
279 Func: compareFunc,
280 Args: args,
281 Posx: tc.n.Pos(),
282 }, nil
283}
284
285func (tc *typeCheckArithmetic) checkLogical(v *TypeCheck, exprs []ast.Type) (ast.Node, error) {
286 for i, t := range exprs {
287 if t != ast.TypeBool {
288 cn := v.ImplicitConversion(t, ast.TypeBool, tc.n.Exprs[i])
289 if cn == nil {
290 return nil, fmt.Errorf(
291 "logical operators require boolean operands, not %s",
292 t,
293 )
294 }
295 tc.n.Exprs[i] = cn
296 }
297 }
298
299 // Return type is always boolean
300 v.StackPush(ast.TypeBool)
301
302 // Arithmetic nodes are replaced with a call to a built-in function
303 args := make([]ast.Node, len(tc.n.Exprs)+1)
304 args[0] = &ast.LiteralNode{
305 Value: tc.n.Op,
306 Typex: ast.TypeInt,
307 Posx: tc.n.Pos(),
308 }
309 copy(args[1:], tc.n.Exprs)
310 return &ast.Call{
311 Func: "__builtin_Logical",
312 Args: args,
313 Posx: tc.n.Pos(),
314 }, nil
315}
316
317type typeCheckCall struct {
318 n *ast.Call
319}
320
321func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) {
322 // Look up the function in the map
323 function, ok := v.Scope.LookupFunc(tc.n.Func)
324 if !ok {
325 return nil, fmt.Errorf("unknown function called: %s", tc.n.Func)
326 }
327
328 // The arguments are on the stack in reverse order, so pop them off.
329 args := make([]ast.Type, len(tc.n.Args))
330 for i, _ := range tc.n.Args {
331 args[len(tc.n.Args)-1-i] = v.StackPop()
332 }
333
334 // Verify the args
335 for i, expected := range function.ArgTypes {
336 if expected == ast.TypeAny {
337 continue
338 }
339
340 if args[i] == ast.TypeUnknown {
341 v.StackPush(ast.TypeUnknown)
342 return tc.n, nil
343 }
344
345 if args[i] != expected {
346 cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i])
347 if cn != nil {
348 tc.n.Args[i] = cn
349 continue
350 }
351
352 return nil, fmt.Errorf(
353 "%s: argument %d should be %s, got %s",
354 tc.n.Func, i+1, expected.Printable(), args[i].Printable())
355 }
356 }
357
358 // If we're variadic, then verify the types there
359 if function.Variadic && function.VariadicType != ast.TypeAny {
360 args = args[len(function.ArgTypes):]
361 for i, t := range args {
362 if t == ast.TypeUnknown {
363 v.StackPush(ast.TypeUnknown)
364 return tc.n, nil
365 }
366
367 if t != function.VariadicType {
368 realI := i + len(function.ArgTypes)
369 cn := v.ImplicitConversion(
370 t, function.VariadicType, tc.n.Args[realI])
371 if cn != nil {
372 tc.n.Args[realI] = cn
373 continue
374 }
375
376 return nil, fmt.Errorf(
377 "%s: argument %d should be %s, got %s",
378 tc.n.Func, realI,
379 function.VariadicType.Printable(), t.Printable())
380 }
381 }
382 }
383
384 // Return type
385 v.StackPush(function.ReturnType)
386
387 return tc.n, nil
388}
389
390type typeCheckConditional struct {
391 n *ast.Conditional
392}
393
394func (tc *typeCheckConditional) TypeCheck(v *TypeCheck) (ast.Node, error) {
395 // On the stack we have the types of the condition, true and false
396 // expressions, but they are in reverse order.
397 falseType := v.StackPop()
398 trueType := v.StackPop()
399 condType := v.StackPop()
400
401 if condType == ast.TypeUnknown {
402 v.StackPush(ast.TypeUnknown)
403 return tc.n, nil
404 }
405
406 if condType != ast.TypeBool {
407 cn := v.ImplicitConversion(condType, ast.TypeBool, tc.n.CondExpr)
408 if cn == nil {
409 return nil, fmt.Errorf(
410 "condition must be type bool, not %s", condType.Printable(),
411 )
412 }
413 tc.n.CondExpr = cn
414 }
415
416 // The types of the true and false expression must match
417 if trueType != falseType && trueType != ast.TypeUnknown && falseType != ast.TypeUnknown {
418
419 // Since passing around stringified versions of other types is
420 // common, we pragmatically allow the false expression to dictate
421 // the result type when the true expression is a string.
422 if trueType == ast.TypeString {
423 cn := v.ImplicitConversion(trueType, falseType, tc.n.TrueExpr)
424 if cn == nil {
425 return nil, fmt.Errorf(
426 "true and false expression types must match; have %s and %s",
427 trueType.Printable(), falseType.Printable(),
428 )
429 }
430 tc.n.TrueExpr = cn
431 trueType = falseType
432 } else {
433 cn := v.ImplicitConversion(falseType, trueType, tc.n.FalseExpr)
434 if cn == nil {
435 return nil, fmt.Errorf(
436 "true and false expression types must match; have %s and %s",
437 trueType.Printable(), falseType.Printable(),
438 )
439 }
440 tc.n.FalseExpr = cn
441 falseType = trueType
442 }
443 }
444
445 // Currently list and map types cannot be used, because we cannot
446 // generally assert that their element types are consistent.
447 // Such support might be added later, either by improving the type
448 // system or restricting usage to only variable and literal expressions,
449 // but for now this is simply prohibited because it doesn't seem to
450 // be a common enough case to be worth the complexity.
451 switch trueType {
452 case ast.TypeList:
453 return nil, fmt.Errorf(
454 "conditional operator cannot be used with list values",
455 )
456 case ast.TypeMap:
457 return nil, fmt.Errorf(
458 "conditional operator cannot be used with map values",
459 )
460 }
461
462 // Result type (guaranteed to also match falseType due to the above)
463 if trueType == ast.TypeUnknown {
464 // falseType may also be unknown, but that's okay because two
465 // unknowns means our result is unknown anyway.
466 v.StackPush(falseType)
467 } else {
468 v.StackPush(trueType)
469 }
470
471 return tc.n, nil
472}
473
474type typeCheckOutput struct {
475 n *ast.Output
476}
477
478func (tc *typeCheckOutput) TypeCheck(v *TypeCheck) (ast.Node, error) {
479 n := tc.n
480 types := make([]ast.Type, len(n.Exprs))
481 for i, _ := range n.Exprs {
482 types[len(n.Exprs)-1-i] = v.StackPop()
483 }
484
485 for _, ty := range types {
486 if ty == ast.TypeUnknown {
487 v.StackPush(ast.TypeUnknown)
488 return tc.n, nil
489 }
490 }
491
492 // If there is only one argument and it is a list, we evaluate to a list
493 if len(types) == 1 {
494 switch t := types[0]; t {
495 case ast.TypeList:
496 fallthrough
497 case ast.TypeMap:
498 v.StackPush(t)
499 return n, nil
500 }
501 }
502
503 // Otherwise, all concat args must be strings, so validate that
504 resultType := ast.TypeString
505 for i, t := range types {
506
507 if t == ast.TypeUnknown {
508 resultType = ast.TypeUnknown
509 continue
510 }
511
512 if t != ast.TypeString {
513 cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i])
514 if cn != nil {
515 n.Exprs[i] = cn
516 continue
517 }
518
519 return nil, fmt.Errorf(
520 "output of an HIL expression must be a string, or a single list (argument %d is %s)", i+1, t)
521 }
522 }
523
524 // This always results in type string, unless there are unknowns
525 v.StackPush(resultType)
526
527 return n, nil
528}
529
530type typeCheckLiteral struct {
531 n *ast.LiteralNode
532}
533
534func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) {
535 v.StackPush(tc.n.Typex)
536 return tc.n, nil
537}
538
539type typeCheckVariableAccess struct {
540 n *ast.VariableAccess
541}
542
543func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) {
544 // Look up the variable in the map
545 variable, ok := v.Scope.LookupVar(tc.n.Name)
546 if !ok {
547 return nil, fmt.Errorf(
548 "unknown variable accessed: %s", tc.n.Name)
549 }
550
551 // Add the type to the stack
552 v.StackPush(variable.Type)
553
554 return tc.n, nil
555}
556
557type typeCheckIndex struct {
558 n *ast.Index
559}
560
561func (tc *typeCheckIndex) TypeCheck(v *TypeCheck) (ast.Node, error) {
562 keyType := v.StackPop()
563 targetType := v.StackPop()
564
565 if keyType == ast.TypeUnknown || targetType == ast.TypeUnknown {
566 v.StackPush(ast.TypeUnknown)
567 return tc.n, nil
568 }
569
570 // Ensure we have a VariableAccess as the target
571 varAccessNode, ok := tc.n.Target.(*ast.VariableAccess)
572 if !ok {
573 return nil, fmt.Errorf(
574 "target of an index must be a VariableAccess node, was %T", tc.n.Target)
575 }
576
577 // Get the variable
578 variable, ok := v.Scope.LookupVar(varAccessNode.Name)
579 if !ok {
580 return nil, fmt.Errorf(
581 "unknown variable accessed: %s", varAccessNode.Name)
582 }
583
584 switch targetType {
585 case ast.TypeList:
586 if keyType != ast.TypeInt {
587 tc.n.Key = v.ImplicitConversion(keyType, ast.TypeInt, tc.n.Key)
588 if tc.n.Key == nil {
589 return nil, fmt.Errorf(
590 "key of an index must be an int, was %s", keyType)
591 }
592 }
593
594 valType, err := ast.VariableListElementTypesAreHomogenous(
595 varAccessNode.Name, variable.Value.([]ast.Variable))
596 if err != nil {
597 return tc.n, err
598 }
599
600 v.StackPush(valType)
601 return tc.n, nil
602 case ast.TypeMap:
603 if keyType != ast.TypeString {
604 tc.n.Key = v.ImplicitConversion(keyType, ast.TypeString, tc.n.Key)
605 if tc.n.Key == nil {
606 return nil, fmt.Errorf(
607 "key of an index must be a string, was %s", keyType)
608 }
609 }
610
611 valType, err := ast.VariableMapValueTypesAreHomogenous(
612 varAccessNode.Name, variable.Value.(map[string]ast.Variable))
613 if err != nil {
614 return tc.n, err
615 }
616
617 v.StackPush(valType)
618 return tc.n, nil
619 default:
620 return nil, fmt.Errorf("invalid index operation into non-indexable type: %s", variable.Type)
621 }
622}
623
624func (v *TypeCheck) ImplicitConversion(
625 actual ast.Type, expected ast.Type, n ast.Node) ast.Node {
626 if v.Implicit == nil {
627 return nil
628 }
629
630 fromMap, ok := v.Implicit[actual]
631 if !ok {
632 return nil
633 }
634
635 toFunc, ok := fromMap[expected]
636 if !ok {
637 return nil
638 }
639
640 return &ast.Call{
641 Func: toFunc,
642 Args: []ast.Node{n},
643 Posx: n.Pos(),
644 }
645}
646
647func (v *TypeCheck) reset() {
648 v.Stack = nil
649 v.err = nil
650}
651
652func (v *TypeCheck) StackPush(t ast.Type) {
653 v.Stack = append(v.Stack, t)
654}
655
656func (v *TypeCheck) StackPop() ast.Type {
657 var x ast.Type
658 x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1]
659 return x
660}
661
662func (v *TypeCheck) StackPeek() ast.Type {
663 if len(v.Stack) == 0 {
664 return ast.TypeInvalid
665 }
666
667 return v.Stack[len(v.Stack)-1]
668}
diff --git a/vendor/github.com/hashicorp/hil/convert.go b/vendor/github.com/hashicorp/hil/convert.go
new file mode 100644
index 0000000..f2024d0
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/convert.go
@@ -0,0 +1,159 @@
1package hil
2
3import (
4 "fmt"
5 "reflect"
6
7 "github.com/hashicorp/hil/ast"
8 "github.com/mitchellh/mapstructure"
9)
10
11// UnknownValue is a sentinel value that can be used to denote
12// that a value of a variable (or map element, list element, etc.)
13// is unknown. This will always have the type ast.TypeUnknown.
14const UnknownValue = "74D93920-ED26-11E3-AC10-0800200C9A66"
15
16var hilMapstructureDecodeHookSlice []interface{}
17var hilMapstructureDecodeHookStringSlice []string
18var hilMapstructureDecodeHookMap map[string]interface{}
19
20// hilMapstructureWeakDecode behaves in the same way as mapstructure.WeakDecode
21// but has a DecodeHook which defeats the backward compatibility mode of mapstructure
22// which WeakDecodes []interface{}{} into an empty map[string]interface{}. This
23// allows us to use WeakDecode (desirable), but not fail on empty lists.
24func hilMapstructureWeakDecode(m interface{}, rawVal interface{}) error {
25 config := &mapstructure.DecoderConfig{
26 DecodeHook: func(source reflect.Type, target reflect.Type, val interface{}) (interface{}, error) {
27 sliceType := reflect.TypeOf(hilMapstructureDecodeHookSlice)
28 stringSliceType := reflect.TypeOf(hilMapstructureDecodeHookStringSlice)
29 mapType := reflect.TypeOf(hilMapstructureDecodeHookMap)
30
31 if (source == sliceType || source == stringSliceType) && target == mapType {
32 return nil, fmt.Errorf("Cannot convert %s into a %s", source, target)
33 }
34
35 return val, nil
36 },
37 WeaklyTypedInput: true,
38 Result: rawVal,
39 }
40
41 decoder, err := mapstructure.NewDecoder(config)
42 if err != nil {
43 return err
44 }
45
46 return decoder.Decode(m)
47}
48
49func InterfaceToVariable(input interface{}) (ast.Variable, error) {
50 if inputVariable, ok := input.(ast.Variable); ok {
51 return inputVariable, nil
52 }
53
54 var stringVal string
55 if err := hilMapstructureWeakDecode(input, &stringVal); err == nil {
56 // Special case the unknown value to turn into "unknown"
57 if stringVal == UnknownValue {
58 return ast.Variable{Value: UnknownValue, Type: ast.TypeUnknown}, nil
59 }
60
61 // Otherwise return the string value
62 return ast.Variable{
63 Type: ast.TypeString,
64 Value: stringVal,
65 }, nil
66 }
67
68 var mapVal map[string]interface{}
69 if err := hilMapstructureWeakDecode(input, &mapVal); err == nil {
70 elements := make(map[string]ast.Variable)
71 for i, element := range mapVal {
72 varElement, err := InterfaceToVariable(element)
73 if err != nil {
74 return ast.Variable{}, err
75 }
76 elements[i] = varElement
77 }
78
79 return ast.Variable{
80 Type: ast.TypeMap,
81 Value: elements,
82 }, nil
83 }
84
85 var sliceVal []interface{}
86 if err := hilMapstructureWeakDecode(input, &sliceVal); err == nil {
87 elements := make([]ast.Variable, len(sliceVal))
88 for i, element := range sliceVal {
89 varElement, err := InterfaceToVariable(element)
90 if err != nil {
91 return ast.Variable{}, err
92 }
93 elements[i] = varElement
94 }
95
96 return ast.Variable{
97 Type: ast.TypeList,
98 Value: elements,
99 }, nil
100 }
101
102 return ast.Variable{}, fmt.Errorf("value for conversion must be a string, interface{} or map[string]interface: got %T", input)
103}
104
105func VariableToInterface(input ast.Variable) (interface{}, error) {
106 if input.Type == ast.TypeString {
107 if inputStr, ok := input.Value.(string); ok {
108 return inputStr, nil
109 } else {
110 return nil, fmt.Errorf("ast.Variable with type string has value which is not a string")
111 }
112 }
113
114 if input.Type == ast.TypeList {
115 inputList, ok := input.Value.([]ast.Variable)
116 if !ok {
117 return nil, fmt.Errorf("ast.Variable with type list has value which is not a []ast.Variable")
118 }
119
120 result := make([]interface{}, 0)
121 if len(inputList) == 0 {
122 return result, nil
123 }
124
125 for _, element := range inputList {
126 if convertedElement, err := VariableToInterface(element); err == nil {
127 result = append(result, convertedElement)
128 } else {
129 return nil, err
130 }
131 }
132
133 return result, nil
134 }
135
136 if input.Type == ast.TypeMap {
137 inputMap, ok := input.Value.(map[string]ast.Variable)
138 if !ok {
139 return nil, fmt.Errorf("ast.Variable with type map has value which is not a map[string]ast.Variable")
140 }
141
142 result := make(map[string]interface{}, 0)
143 if len(inputMap) == 0 {
144 return result, nil
145 }
146
147 for key, value := range inputMap {
148 if convertedValue, err := VariableToInterface(value); err == nil {
149 result[key] = convertedValue
150 } else {
151 return nil, err
152 }
153 }
154
155 return result, nil
156 }
157
158 return nil, fmt.Errorf("unknown input type: %s", input.Type)
159}
diff --git a/vendor/github.com/hashicorp/hil/eval.go b/vendor/github.com/hashicorp/hil/eval.go
new file mode 100644
index 0000000..2782076
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/eval.go
@@ -0,0 +1,472 @@
1package hil
2
3import (
4 "bytes"
5 "errors"
6 "fmt"
7 "sync"
8
9 "github.com/hashicorp/hil/ast"
10)
11
12// EvalConfig is the configuration for evaluating.
13type EvalConfig struct {
14 // GlobalScope is the global scope of execution for evaluation.
15 GlobalScope *ast.BasicScope
16
17 // SemanticChecks is a list of additional semantic checks that will be run
18 // on the tree prior to evaluating it. The type checker, identifier checker,
19 // etc. will be run before these automatically.
20 SemanticChecks []SemanticChecker
21}
22
23// SemanticChecker is the type that must be implemented to do a
24// semantic check on an AST tree. This will be called with the root node.
25type SemanticChecker func(ast.Node) error
26
27// EvaluationResult is a struct returned from the hil.Eval function,
28// representing the result of an interpolation. Results are returned in their
29// "natural" Go structure rather than in terms of the HIL AST. For the types
30// currently implemented, this means that the Value field can be interpreted as
31// the following Go types:
32// TypeInvalid: undefined
33// TypeString: string
34// TypeList: []interface{}
35// TypeMap: map[string]interface{}
36// TypBool: bool
37type EvaluationResult struct {
38 Type EvalType
39 Value interface{}
40}
41
42// InvalidResult is a structure representing the result of a HIL interpolation
43// which has invalid syntax, missing variables, or some other type of error.
44// The error is described out of band in the accompanying error return value.
45var InvalidResult = EvaluationResult{Type: TypeInvalid, Value: nil}
46
47// errExitUnknown is an internal error that when returned means the result
48// is an unknown value. We use this for early exit.
49var errExitUnknown = errors.New("unknown value")
50
51func Eval(root ast.Node, config *EvalConfig) (EvaluationResult, error) {
52 output, outputType, err := internalEval(root, config)
53 if err != nil {
54 return InvalidResult, err
55 }
56
57 // If the result contains any nested unknowns then the result as a whole
58 // is unknown, so that callers only have to deal with "entirely known"
59 // or "entirely unknown" as outcomes.
60 if ast.IsUnknown(ast.Variable{Type: outputType, Value: output}) {
61 outputType = ast.TypeUnknown
62 output = UnknownValue
63 }
64
65 switch outputType {
66 case ast.TypeList:
67 val, err := VariableToInterface(ast.Variable{
68 Type: ast.TypeList,
69 Value: output,
70 })
71 return EvaluationResult{
72 Type: TypeList,
73 Value: val,
74 }, err
75 case ast.TypeMap:
76 val, err := VariableToInterface(ast.Variable{
77 Type: ast.TypeMap,
78 Value: output,
79 })
80 return EvaluationResult{
81 Type: TypeMap,
82 Value: val,
83 }, err
84 case ast.TypeString:
85 return EvaluationResult{
86 Type: TypeString,
87 Value: output,
88 }, nil
89 case ast.TypeBool:
90 return EvaluationResult{
91 Type: TypeBool,
92 Value: output,
93 }, nil
94 case ast.TypeUnknown:
95 return EvaluationResult{
96 Type: TypeUnknown,
97 Value: UnknownValue,
98 }, nil
99 default:
100 return InvalidResult, fmt.Errorf("unknown type %s as interpolation output", outputType)
101 }
102}
103
104// Eval evaluates the given AST tree and returns its output value, the type
105// of the output, and any error that occurred.
106func internalEval(root ast.Node, config *EvalConfig) (interface{}, ast.Type, error) {
107 // Copy the scope so we can add our builtins
108 if config == nil {
109 config = new(EvalConfig)
110 }
111 scope := registerBuiltins(config.GlobalScope)
112 implicitMap := map[ast.Type]map[ast.Type]string{
113 ast.TypeFloat: {
114 ast.TypeInt: "__builtin_FloatToInt",
115 ast.TypeString: "__builtin_FloatToString",
116 },
117 ast.TypeInt: {
118 ast.TypeFloat: "__builtin_IntToFloat",
119 ast.TypeString: "__builtin_IntToString",
120 },
121 ast.TypeString: {
122 ast.TypeInt: "__builtin_StringToInt",
123 ast.TypeFloat: "__builtin_StringToFloat",
124 ast.TypeBool: "__builtin_StringToBool",
125 },
126 ast.TypeBool: {
127 ast.TypeString: "__builtin_BoolToString",
128 },
129 }
130
131 // Build our own semantic checks that we always run
132 tv := &TypeCheck{Scope: scope, Implicit: implicitMap}
133 ic := &IdentifierCheck{Scope: scope}
134
135 // Build up the semantic checks for execution
136 checks := make(
137 []SemanticChecker,
138 len(config.SemanticChecks),
139 len(config.SemanticChecks)+2)
140 copy(checks, config.SemanticChecks)
141 checks = append(checks, ic.Visit)
142 checks = append(checks, tv.Visit)
143
144 // Run the semantic checks
145 for _, check := range checks {
146 if err := check(root); err != nil {
147 return nil, ast.TypeInvalid, err
148 }
149 }
150
151 // Execute
152 v := &evalVisitor{Scope: scope}
153 return v.Visit(root)
154}
155
156// EvalNode is the interface that must be implemented by any ast.Node
157// to support evaluation. This will be called in visitor pattern order.
158// The result of each call to Eval is automatically pushed onto the
159// stack as a LiteralNode. Pop elements off the stack to get child
160// values.
161type EvalNode interface {
162 Eval(ast.Scope, *ast.Stack) (interface{}, ast.Type, error)
163}
164
165type evalVisitor struct {
166 Scope ast.Scope
167 Stack ast.Stack
168
169 err error
170 lock sync.Mutex
171}
172
173func (v *evalVisitor) Visit(root ast.Node) (interface{}, ast.Type, error) {
174 // Run the actual visitor pattern
175 root.Accept(v.visit)
176
177 // Get our result and clear out everything else
178 var result *ast.LiteralNode
179 if v.Stack.Len() > 0 {
180 result = v.Stack.Pop().(*ast.LiteralNode)
181 } else {
182 result = new(ast.LiteralNode)
183 }
184 resultErr := v.err
185 if resultErr == errExitUnknown {
186 // This means the return value is unknown and we used the error
187 // as an early exit mechanism. Reset since the value on the stack
188 // should be the unknown value.
189 resultErr = nil
190 }
191
192 // Clear everything else so we aren't just dangling
193 v.Stack.Reset()
194 v.err = nil
195
196 t, err := result.Type(v.Scope)
197 if err != nil {
198 return nil, ast.TypeInvalid, err
199 }
200
201 return result.Value, t, resultErr
202}
203
204func (v *evalVisitor) visit(raw ast.Node) ast.Node {
205 if v.err != nil {
206 return raw
207 }
208
209 en, err := evalNode(raw)
210 if err != nil {
211 v.err = err
212 return raw
213 }
214
215 out, outType, err := en.Eval(v.Scope, &v.Stack)
216 if err != nil {
217 v.err = err
218 return raw
219 }
220
221 v.Stack.Push(&ast.LiteralNode{
222 Value: out,
223 Typex: outType,
224 })
225
226 if outType == ast.TypeUnknown {
227 // Halt immediately
228 v.err = errExitUnknown
229 return raw
230 }
231
232 return raw
233}
234
235// evalNode is a private function that returns an EvalNode for built-in
236// types as well as any other EvalNode implementations.
237func evalNode(raw ast.Node) (EvalNode, error) {
238 switch n := raw.(type) {
239 case *ast.Index:
240 return &evalIndex{n}, nil
241 case *ast.Call:
242 return &evalCall{n}, nil
243 case *ast.Conditional:
244 return &evalConditional{n}, nil
245 case *ast.Output:
246 return &evalOutput{n}, nil
247 case *ast.LiteralNode:
248 return &evalLiteralNode{n}, nil
249 case *ast.VariableAccess:
250 return &evalVariableAccess{n}, nil
251 default:
252 en, ok := n.(EvalNode)
253 if !ok {
254 return nil, fmt.Errorf("node doesn't support evaluation: %#v", raw)
255 }
256
257 return en, nil
258 }
259}
260
261type evalCall struct{ *ast.Call }
262
263func (v *evalCall) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) {
264 // Look up the function in the map
265 function, ok := s.LookupFunc(v.Func)
266 if !ok {
267 return nil, ast.TypeInvalid, fmt.Errorf(
268 "unknown function called: %s", v.Func)
269 }
270
271 // The arguments are on the stack in reverse order, so pop them off.
272 args := make([]interface{}, len(v.Args))
273 for i, _ := range v.Args {
274 node := stack.Pop().(*ast.LiteralNode)
275 if node.IsUnknown() {
276 // If any arguments are unknown then the result is automatically unknown
277 return UnknownValue, ast.TypeUnknown, nil
278 }
279 args[len(v.Args)-1-i] = node.Value
280 }
281
282 // Call the function
283 result, err := function.Callback(args)
284 if err != nil {
285 return nil, ast.TypeInvalid, fmt.Errorf("%s: %s", v.Func, err)
286 }
287
288 return result, function.ReturnType, nil
289}
290
291type evalConditional struct{ *ast.Conditional }
292
293func (v *evalConditional) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) {
294 // On the stack we have literal nodes representing the resulting values
295 // of the condition, true and false expressions, but they are in reverse
296 // order.
297 falseLit := stack.Pop().(*ast.LiteralNode)
298 trueLit := stack.Pop().(*ast.LiteralNode)
299 condLit := stack.Pop().(*ast.LiteralNode)
300
301 if condLit.IsUnknown() {
302 // If our conditional is unknown then our result is also unknown
303 return UnknownValue, ast.TypeUnknown, nil
304 }
305
306 if condLit.Value.(bool) {
307 return trueLit.Value, trueLit.Typex, nil
308 } else {
309 return falseLit.Value, trueLit.Typex, nil
310 }
311}
312
313type evalIndex struct{ *ast.Index }
314
315func (v *evalIndex) Eval(scope ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) {
316 key := stack.Pop().(*ast.LiteralNode)
317 target := stack.Pop().(*ast.LiteralNode)
318
319 variableName := v.Index.Target.(*ast.VariableAccess).Name
320
321 if key.IsUnknown() {
322 // If our key is unknown then our result is also unknown
323 return UnknownValue, ast.TypeUnknown, nil
324 }
325
326 // For target, we'll accept collections containing unknown values but
327 // we still need to catch when the collection itself is unknown, shallowly.
328 if target.Typex == ast.TypeUnknown {
329 return UnknownValue, ast.TypeUnknown, nil
330 }
331
332 switch target.Typex {
333 case ast.TypeList:
334 return v.evalListIndex(variableName, target.Value, key.Value)
335 case ast.TypeMap:
336 return v.evalMapIndex(variableName, target.Value, key.Value)
337 default:
338 return nil, ast.TypeInvalid, fmt.Errorf(
339 "target %q for indexing must be ast.TypeList or ast.TypeMap, is %s",
340 variableName, target.Typex)
341 }
342}
343
344func (v *evalIndex) evalListIndex(variableName string, target interface{}, key interface{}) (interface{}, ast.Type, error) {
345 // We assume type checking was already done and we can assume that target
346 // is a list and key is an int
347 list, ok := target.([]ast.Variable)
348 if !ok {
349 return nil, ast.TypeInvalid, fmt.Errorf(
350 "cannot cast target to []Variable, is: %T", target)
351 }
352
353 keyInt, ok := key.(int)
354 if !ok {
355 return nil, ast.TypeInvalid, fmt.Errorf(
356 "cannot cast key to int, is: %T", key)
357 }
358
359 if len(list) == 0 {
360 return nil, ast.TypeInvalid, fmt.Errorf("list is empty")
361 }
362
363 if keyInt < 0 || len(list) < keyInt+1 {
364 return nil, ast.TypeInvalid, fmt.Errorf(
365 "index %d out of range for list %s (max %d)",
366 keyInt, variableName, len(list))
367 }
368
369 returnVal := list[keyInt].Value
370 returnType := list[keyInt].Type
371 return returnVal, returnType, nil
372}
373
374func (v *evalIndex) evalMapIndex(variableName string, target interface{}, key interface{}) (interface{}, ast.Type, error) {
375 // We assume type checking was already done and we can assume that target
376 // is a map and key is a string
377 vmap, ok := target.(map[string]ast.Variable)
378 if !ok {
379 return nil, ast.TypeInvalid, fmt.Errorf(
380 "cannot cast target to map[string]Variable, is: %T", target)
381 }
382
383 keyString, ok := key.(string)
384 if !ok {
385 return nil, ast.TypeInvalid, fmt.Errorf(
386 "cannot cast key to string, is: %T", key)
387 }
388
389 if len(vmap) == 0 {
390 return nil, ast.TypeInvalid, fmt.Errorf("map is empty")
391 }
392
393 value, ok := vmap[keyString]
394 if !ok {
395 return nil, ast.TypeInvalid, fmt.Errorf(
396 "key %q does not exist in map %s", keyString, variableName)
397 }
398
399 return value.Value, value.Type, nil
400}
401
402type evalOutput struct{ *ast.Output }
403
404func (v *evalOutput) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) {
405 // The expressions should all be on the stack in reverse
406 // order. So pop them off, reverse their order, and concatenate.
407 nodes := make([]*ast.LiteralNode, 0, len(v.Exprs))
408 haveUnknown := false
409 for range v.Exprs {
410 n := stack.Pop().(*ast.LiteralNode)
411 nodes = append(nodes, n)
412
413 // If we have any unknowns then the whole result is unknown
414 // (we must deal with this first, because the type checker can
415 // skip type conversions in the presence of unknowns, and thus
416 // any of our other nodes may be incorrectly typed.)
417 if n.IsUnknown() {
418 haveUnknown = true
419 }
420 }
421
422 if haveUnknown {
423 return UnknownValue, ast.TypeUnknown, nil
424 }
425
426 // Special case the single list and map
427 if len(nodes) == 1 {
428 switch t := nodes[0].Typex; t {
429 case ast.TypeList:
430 fallthrough
431 case ast.TypeMap:
432 fallthrough
433 case ast.TypeUnknown:
434 return nodes[0].Value, t, nil
435 }
436 }
437
438 // Otherwise concatenate the strings
439 var buf bytes.Buffer
440 for i := len(nodes) - 1; i >= 0; i-- {
441 if nodes[i].Typex != ast.TypeString {
442 return nil, ast.TypeInvalid, fmt.Errorf(
443 "invalid output with %s value at index %d: %#v",
444 nodes[i].Typex,
445 i,
446 nodes[i].Value,
447 )
448 }
449 buf.WriteString(nodes[i].Value.(string))
450 }
451
452 return buf.String(), ast.TypeString, nil
453}
454
455type evalLiteralNode struct{ *ast.LiteralNode }
456
457func (v *evalLiteralNode) Eval(ast.Scope, *ast.Stack) (interface{}, ast.Type, error) {
458 return v.Value, v.Typex, nil
459}
460
461type evalVariableAccess struct{ *ast.VariableAccess }
462
463func (v *evalVariableAccess) Eval(scope ast.Scope, _ *ast.Stack) (interface{}, ast.Type, error) {
464 // Look up the variable in the map
465 variable, ok := scope.LookupVar(v.Name)
466 if !ok {
467 return nil, ast.TypeInvalid, fmt.Errorf(
468 "unknown variable accessed: %s", v.Name)
469 }
470
471 return variable.Value, variable.Type, nil
472}
diff --git a/vendor/github.com/hashicorp/hil/eval_type.go b/vendor/github.com/hashicorp/hil/eval_type.go
new file mode 100644
index 0000000..6946ecd
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/eval_type.go
@@ -0,0 +1,16 @@
1package hil
2
3//go:generate stringer -type=EvalType eval_type.go
4
5// EvalType represents the type of the output returned from a HIL
6// evaluation.
7type EvalType uint32
8
9const (
10 TypeInvalid EvalType = 0
11 TypeString EvalType = 1 << iota
12 TypeBool
13 TypeList
14 TypeMap
15 TypeUnknown
16)
diff --git a/vendor/github.com/hashicorp/hil/evaltype_string.go b/vendor/github.com/hashicorp/hil/evaltype_string.go
new file mode 100644
index 0000000..b107ddd
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/evaltype_string.go
@@ -0,0 +1,42 @@
1// Code generated by "stringer -type=EvalType eval_type.go"; DO NOT EDIT
2
3package hil
4
5import "fmt"
6
7const (
8 _EvalType_name_0 = "TypeInvalid"
9 _EvalType_name_1 = "TypeString"
10 _EvalType_name_2 = "TypeBool"
11 _EvalType_name_3 = "TypeList"
12 _EvalType_name_4 = "TypeMap"
13 _EvalType_name_5 = "TypeUnknown"
14)
15
16var (
17 _EvalType_index_0 = [...]uint8{0, 11}
18 _EvalType_index_1 = [...]uint8{0, 10}
19 _EvalType_index_2 = [...]uint8{0, 8}
20 _EvalType_index_3 = [...]uint8{0, 8}
21 _EvalType_index_4 = [...]uint8{0, 7}
22 _EvalType_index_5 = [...]uint8{0, 11}
23)
24
25func (i EvalType) String() string {
26 switch {
27 case i == 0:
28 return _EvalType_name_0
29 case i == 2:
30 return _EvalType_name_1
31 case i == 4:
32 return _EvalType_name_2
33 case i == 8:
34 return _EvalType_name_3
35 case i == 16:
36 return _EvalType_name_4
37 case i == 32:
38 return _EvalType_name_5
39 default:
40 return fmt.Sprintf("EvalType(%d)", i)
41 }
42}
diff --git a/vendor/github.com/hashicorp/hil/parse.go b/vendor/github.com/hashicorp/hil/parse.go
new file mode 100644
index 0000000..ecbe1fd
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/parse.go
@@ -0,0 +1,29 @@
1package hil
2
3import (
4 "github.com/hashicorp/hil/ast"
5 "github.com/hashicorp/hil/parser"
6 "github.com/hashicorp/hil/scanner"
7)
8
9// Parse parses the given program and returns an executable AST tree.
10//
11// Syntax errors are returned with error having the dynamic type
12// *parser.ParseError, which gives the caller access to the source position
13// where the error was found, which allows (for example) combining it with
14// a known source filename to add context to the error message.
15func Parse(v string) (ast.Node, error) {
16 return ParseWithPosition(v, ast.Pos{Line: 1, Column: 1})
17}
18
19// ParseWithPosition is like Parse except that it overrides the source
20// row and column position of the first character in the string, which should
21// be 1-based.
22//
23// This can be used when HIL is embedded in another language and the outer
24// parser knows the row and column where the HIL expression started within
25// the overall source file.
26func ParseWithPosition(v string, pos ast.Pos) (ast.Node, error) {
27 ch := scanner.Scan(v, pos)
28 return parser.Parse(ch)
29}
diff --git a/vendor/github.com/hashicorp/hil/parser/binary_op.go b/vendor/github.com/hashicorp/hil/parser/binary_op.go
new file mode 100644
index 0000000..2e013e0
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/parser/binary_op.go
@@ -0,0 +1,45 @@
1package parser
2
3import (
4 "github.com/hashicorp/hil/ast"
5 "github.com/hashicorp/hil/scanner"
6)
7
8var binaryOps []map[scanner.TokenType]ast.ArithmeticOp
9
10func init() {
11 // This operation table maps from the operator's scanner token type
12 // to the AST arithmetic operation. All expressions produced from
13 // binary operators are *ast.Arithmetic nodes.
14 //
15 // Binary operator groups are listed in order of precedence, with
16 // the *lowest* precedence first. Operators within the same group
17 // have left-to-right associativity.
18 binaryOps = []map[scanner.TokenType]ast.ArithmeticOp{
19 {
20 scanner.OR: ast.ArithmeticOpLogicalOr,
21 },
22 {
23 scanner.AND: ast.ArithmeticOpLogicalAnd,
24 },
25 {
26 scanner.EQUAL: ast.ArithmeticOpEqual,
27 scanner.NOTEQUAL: ast.ArithmeticOpNotEqual,
28 },
29 {
30 scanner.GT: ast.ArithmeticOpGreaterThan,
31 scanner.GTE: ast.ArithmeticOpGreaterThanOrEqual,
32 scanner.LT: ast.ArithmeticOpLessThan,
33 scanner.LTE: ast.ArithmeticOpLessThanOrEqual,
34 },
35 {
36 scanner.PLUS: ast.ArithmeticOpAdd,
37 scanner.MINUS: ast.ArithmeticOpSub,
38 },
39 {
40 scanner.STAR: ast.ArithmeticOpMul,
41 scanner.SLASH: ast.ArithmeticOpDiv,
42 scanner.PERCENT: ast.ArithmeticOpMod,
43 },
44 }
45}
diff --git a/vendor/github.com/hashicorp/hil/parser/error.go b/vendor/github.com/hashicorp/hil/parser/error.go
new file mode 100644
index 0000000..bacd696
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/parser/error.go
@@ -0,0 +1,38 @@
1package parser
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/hil/ast"
7 "github.com/hashicorp/hil/scanner"
8)
9
10type ParseError struct {
11 Message string
12 Pos ast.Pos
13}
14
15func Errorf(pos ast.Pos, format string, args ...interface{}) error {
16 return &ParseError{
17 Message: fmt.Sprintf(format, args...),
18 Pos: pos,
19 }
20}
21
22// TokenErrorf is a convenient wrapper around Errorf that uses the
23// position of the given token.
24func TokenErrorf(token *scanner.Token, format string, args ...interface{}) error {
25 return Errorf(token.Pos, format, args...)
26}
27
28func ExpectationError(wanted string, got *scanner.Token) error {
29 return TokenErrorf(got, "expected %s but found %s", wanted, got)
30}
31
32func (e *ParseError) Error() string {
33 return fmt.Sprintf("parse error at %s: %s", e.Pos, e.Message)
34}
35
36func (e *ParseError) String() string {
37 return e.Error()
38}
diff --git a/vendor/github.com/hashicorp/hil/parser/fuzz.go b/vendor/github.com/hashicorp/hil/parser/fuzz.go
new file mode 100644
index 0000000..de954f3
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/parser/fuzz.go
@@ -0,0 +1,28 @@
1// +build gofuzz
2
3package parser
4
5import (
6 "github.com/hashicorp/hil/ast"
7 "github.com/hashicorp/hil/scanner"
8)
9
10// This is a fuzz testing function designed to be used with go-fuzz:
11// https://github.com/dvyukov/go-fuzz
12//
13// It's not included in a normal build due to the gofuzz build tag above.
14//
15// There are some input files that you can use as a seed corpus for go-fuzz
16// in the directory ./fuzz-corpus .
17
18func Fuzz(data []byte) int {
19 str := string(data)
20
21 ch := scanner.Scan(str, ast.Pos{Line: 1, Column: 1})
22 _, err := Parse(ch)
23 if err != nil {
24 return 0
25 }
26
27 return 1
28}
diff --git a/vendor/github.com/hashicorp/hil/parser/parser.go b/vendor/github.com/hashicorp/hil/parser/parser.go
new file mode 100644
index 0000000..376f1c4
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/parser/parser.go
@@ -0,0 +1,522 @@
1package parser
2
3import (
4 "strconv"
5 "unicode/utf8"
6
7 "github.com/hashicorp/hil/ast"
8 "github.com/hashicorp/hil/scanner"
9)
10
11func Parse(ch <-chan *scanner.Token) (ast.Node, error) {
12 peeker := scanner.NewPeeker(ch)
13 parser := &parser{peeker}
14 output, err := parser.ParseTopLevel()
15 peeker.Close()
16 return output, err
17}
18
19type parser struct {
20 peeker *scanner.Peeker
21}
22
23func (p *parser) ParseTopLevel() (ast.Node, error) {
24 return p.parseInterpolationSeq(false)
25}
26
27func (p *parser) ParseQuoted() (ast.Node, error) {
28 return p.parseInterpolationSeq(true)
29}
30
31// parseInterpolationSeq parses either the top-level sequence of literals
32// and interpolation expressions or a similar sequence within a quoted
33// string inside an interpolation expression. The latter case is requested
34// by setting 'quoted' to true.
35func (p *parser) parseInterpolationSeq(quoted bool) (ast.Node, error) {
36 literalType := scanner.LITERAL
37 endType := scanner.EOF
38 if quoted {
39 // exceptions for quoted sequences
40 literalType = scanner.STRING
41 endType = scanner.CQUOTE
42 }
43
44 startPos := p.peeker.Peek().Pos
45
46 if quoted {
47 tok := p.peeker.Read()
48 if tok.Type != scanner.OQUOTE {
49 return nil, ExpectationError("open quote", tok)
50 }
51 }
52
53 var exprs []ast.Node
54 for {
55 tok := p.peeker.Read()
56
57 if tok.Type == endType {
58 break
59 }
60
61 switch tok.Type {
62 case literalType:
63 val, err := p.parseStringToken(tok)
64 if err != nil {
65 return nil, err
66 }
67 exprs = append(exprs, &ast.LiteralNode{
68 Value: val,
69 Typex: ast.TypeString,
70 Posx: tok.Pos,
71 })
72 case scanner.BEGIN:
73 expr, err := p.ParseInterpolation()
74 if err != nil {
75 return nil, err
76 }
77 exprs = append(exprs, expr)
78 default:
79 return nil, ExpectationError(`"${"`, tok)
80 }
81 }
82
83 if len(exprs) == 0 {
84 // If we have no parts at all then the input must've
85 // been an empty string.
86 exprs = append(exprs, &ast.LiteralNode{
87 Value: "",
88 Typex: ast.TypeString,
89 Posx: startPos,
90 })
91 }
92
93 // As a special case, if our "Output" contains only one expression
94 // and it's a literal string then we'll hoist it up to be our
95 // direct return value, so callers can easily recognize a string
96 // that has no interpolations at all.
97 if len(exprs) == 1 {
98 if lit, ok := exprs[0].(*ast.LiteralNode); ok {
99 if lit.Typex == ast.TypeString {
100 return lit, nil
101 }
102 }
103 }
104
105 return &ast.Output{
106 Exprs: exprs,
107 Posx: startPos,
108 }, nil
109}
110
111// parseStringToken takes a token of either LITERAL or STRING type and
112// returns the interpreted string, after processing any relevant
113// escape sequences.
114func (p *parser) parseStringToken(tok *scanner.Token) (string, error) {
115 var backslashes bool
116 switch tok.Type {
117 case scanner.LITERAL:
118 backslashes = false
119 case scanner.STRING:
120 backslashes = true
121 default:
122 panic("unsupported string token type")
123 }
124
125 raw := []byte(tok.Content)
126 buf := make([]byte, 0, len(raw))
127
128 for i := 0; i < len(raw); i++ {
129 b := raw[i]
130 more := len(raw) > (i + 1)
131
132 if b == '$' {
133 if more && raw[i+1] == '$' {
134 // skip over the second dollar sign
135 i++
136 }
137 } else if backslashes && b == '\\' {
138 if !more {
139 return "", Errorf(
140 ast.Pos{
141 Column: tok.Pos.Column + utf8.RuneCount(raw[:i]),
142 Line: tok.Pos.Line,
143 },
144 `unfinished backslash escape sequence`,
145 )
146 }
147 escapeType := raw[i+1]
148 switch escapeType {
149 case '\\':
150 // skip over the second slash
151 i++
152 case 'n':
153 b = '\n'
154 i++
155 case '"':
156 b = '"'
157 i++
158 default:
159 return "", Errorf(
160 ast.Pos{
161 Column: tok.Pos.Column + utf8.RuneCount(raw[:i]),
162 Line: tok.Pos.Line,
163 },
164 `invalid backslash escape sequence`,
165 )
166 }
167 }
168
169 buf = append(buf, b)
170 }
171
172 return string(buf), nil
173}
174
175func (p *parser) ParseInterpolation() (ast.Node, error) {
176 // By the time we're called, we're already "inside" the ${ sequence
177 // because the caller consumed the ${ token.
178
179 expr, err := p.ParseExpression()
180 if err != nil {
181 return nil, err
182 }
183
184 err = p.requireTokenType(scanner.END, `"}"`)
185 if err != nil {
186 return nil, err
187 }
188
189 return expr, nil
190}
191
192func (p *parser) ParseExpression() (ast.Node, error) {
193 return p.parseTernaryCond()
194}
195
196func (p *parser) parseTernaryCond() (ast.Node, error) {
197 // The ternary condition operator (.. ? .. : ..) behaves somewhat
198 // like a binary operator except that the "operator" is itself
199 // an expression enclosed in two punctuation characters.
200 // The middle expression is parsed as if the ? and : symbols
201 // were parentheses. The "rhs" (the "false expression") is then
202 // treated right-associatively so it behaves similarly to the
203 // middle in terms of precedence.
204
205 startPos := p.peeker.Peek().Pos
206
207 var cond, trueExpr, falseExpr ast.Node
208 var err error
209
210 cond, err = p.parseBinaryOps(binaryOps)
211 if err != nil {
212 return nil, err
213 }
214
215 next := p.peeker.Peek()
216 if next.Type != scanner.QUESTION {
217 return cond, nil
218 }
219
220 p.peeker.Read() // eat question mark
221
222 trueExpr, err = p.ParseExpression()
223 if err != nil {
224 return nil, err
225 }
226
227 colon := p.peeker.Read()
228 if colon.Type != scanner.COLON {
229 return nil, ExpectationError(":", colon)
230 }
231
232 falseExpr, err = p.ParseExpression()
233 if err != nil {
234 return nil, err
235 }
236
237 return &ast.Conditional{
238 CondExpr: cond,
239 TrueExpr: trueExpr,
240 FalseExpr: falseExpr,
241 Posx: startPos,
242 }, nil
243}
244
245// parseBinaryOps calls itself recursively to work through all of the
246// operator precedence groups, and then eventually calls ParseExpressionTerm
247// for each operand.
248func (p *parser) parseBinaryOps(ops []map[scanner.TokenType]ast.ArithmeticOp) (ast.Node, error) {
249 if len(ops) == 0 {
250 // We've run out of operators, so now we'll just try to parse a term.
251 return p.ParseExpressionTerm()
252 }
253
254 thisLevel := ops[0]
255 remaining := ops[1:]
256
257 startPos := p.peeker.Peek().Pos
258
259 var lhs, rhs ast.Node
260 operator := ast.ArithmeticOpInvalid
261 var err error
262
263 // parse a term that might be the first operand of a binary
264 // expression or it might just be a standalone term, but
265 // we won't know until we've parsed it and can look ahead
266 // to see if there's an operator token.
267 lhs, err = p.parseBinaryOps(remaining)
268 if err != nil {
269 return nil, err
270 }
271
272 // We'll keep eating up arithmetic operators until we run
273 // out, so that operators with the same precedence will combine in a
274 // left-associative manner:
275 // a+b+c => (a+b)+c, not a+(b+c)
276 //
277 // Should we later want to have right-associative operators, a way
278 // to achieve that would be to call back up to ParseExpression here
279 // instead of iteratively parsing only the remaining operators.
280 for {
281 next := p.peeker.Peek()
282 var newOperator ast.ArithmeticOp
283 var ok bool
284 if newOperator, ok = thisLevel[next.Type]; !ok {
285 break
286 }
287
288 // Are we extending an expression started on
289 // the previous iteration?
290 if operator != ast.ArithmeticOpInvalid {
291 lhs = &ast.Arithmetic{
292 Op: operator,
293 Exprs: []ast.Node{lhs, rhs},
294 Posx: startPos,
295 }
296 }
297
298 operator = newOperator
299 p.peeker.Read() // eat operator token
300 rhs, err = p.parseBinaryOps(remaining)
301 if err != nil {
302 return nil, err
303 }
304 }
305
306 if operator != ast.ArithmeticOpInvalid {
307 return &ast.Arithmetic{
308 Op: operator,
309 Exprs: []ast.Node{lhs, rhs},
310 Posx: startPos,
311 }, nil
312 } else {
313 return lhs, nil
314 }
315}
316
317func (p *parser) ParseExpressionTerm() (ast.Node, error) {
318
319 next := p.peeker.Peek()
320
321 switch next.Type {
322
323 case scanner.OPAREN:
324 p.peeker.Read()
325 expr, err := p.ParseExpression()
326 if err != nil {
327 return nil, err
328 }
329 err = p.requireTokenType(scanner.CPAREN, `")"`)
330 return expr, err
331
332 case scanner.OQUOTE:
333 return p.ParseQuoted()
334
335 case scanner.INTEGER:
336 tok := p.peeker.Read()
337 val, err := strconv.Atoi(tok.Content)
338 if err != nil {
339 return nil, TokenErrorf(tok, "invalid integer: %s", err)
340 }
341 return &ast.LiteralNode{
342 Value: val,
343 Typex: ast.TypeInt,
344 Posx: tok.Pos,
345 }, nil
346
347 case scanner.FLOAT:
348 tok := p.peeker.Read()
349 val, err := strconv.ParseFloat(tok.Content, 64)
350 if err != nil {
351 return nil, TokenErrorf(tok, "invalid float: %s", err)
352 }
353 return &ast.LiteralNode{
354 Value: val,
355 Typex: ast.TypeFloat,
356 Posx: tok.Pos,
357 }, nil
358
359 case scanner.BOOL:
360 tok := p.peeker.Read()
361 // the scanner guarantees that tok.Content is either "true" or "false"
362 var val bool
363 if tok.Content[0] == 't' {
364 val = true
365 } else {
366 val = false
367 }
368 return &ast.LiteralNode{
369 Value: val,
370 Typex: ast.TypeBool,
371 Posx: tok.Pos,
372 }, nil
373
374 case scanner.MINUS:
375 opTok := p.peeker.Read()
376 // important to use ParseExpressionTerm rather than ParseExpression
377 // here, otherwise we can capture a following binary expression into
378 // our negation.
379 // e.g. -46+5 should parse as (0-46)+5, not 0-(46+5)
380 operand, err := p.ParseExpressionTerm()
381 if err != nil {
382 return nil, err
383 }
384 // The AST currently represents negative numbers as
385 // a binary subtraction of the number from zero.
386 return &ast.Arithmetic{
387 Op: ast.ArithmeticOpSub,
388 Exprs: []ast.Node{
389 &ast.LiteralNode{
390 Value: 0,
391 Typex: ast.TypeInt,
392 Posx: opTok.Pos,
393 },
394 operand,
395 },
396 Posx: opTok.Pos,
397 }, nil
398
399 case scanner.BANG:
400 opTok := p.peeker.Read()
401 // important to use ParseExpressionTerm rather than ParseExpression
402 // here, otherwise we can capture a following binary expression into
403 // our negation.
404 operand, err := p.ParseExpressionTerm()
405 if err != nil {
406 return nil, err
407 }
408 // The AST currently represents binary negation as an equality
409 // test with "false".
410 return &ast.Arithmetic{
411 Op: ast.ArithmeticOpEqual,
412 Exprs: []ast.Node{
413 &ast.LiteralNode{
414 Value: false,
415 Typex: ast.TypeBool,
416 Posx: opTok.Pos,
417 },
418 operand,
419 },
420 Posx: opTok.Pos,
421 }, nil
422
423 case scanner.IDENTIFIER:
424 return p.ParseScopeInteraction()
425
426 default:
427 return nil, ExpectationError("expression", next)
428 }
429}
430
431// ParseScopeInteraction parses the expression types that interact
432// with the evaluation scope: variable access, function calls, and
433// indexing.
434//
435// Indexing should actually be a distinct operator in its own right,
436// so that e.g. it can be applied to the result of a function call,
437// but for now we're preserving the behavior of the older yacc-based
438// parser.
439func (p *parser) ParseScopeInteraction() (ast.Node, error) {
440 first := p.peeker.Read()
441 startPos := first.Pos
442 if first.Type != scanner.IDENTIFIER {
443 return nil, ExpectationError("identifier", first)
444 }
445
446 next := p.peeker.Peek()
447 if next.Type == scanner.OPAREN {
448 // function call
449 funcName := first.Content
450 p.peeker.Read() // eat paren
451 var args []ast.Node
452
453 for {
454 if p.peeker.Peek().Type == scanner.CPAREN {
455 break
456 }
457
458 arg, err := p.ParseExpression()
459 if err != nil {
460 return nil, err
461 }
462
463 args = append(args, arg)
464
465 if p.peeker.Peek().Type == scanner.COMMA {
466 p.peeker.Read() // eat comma
467 continue
468 } else {
469 break
470 }
471 }
472
473 err := p.requireTokenType(scanner.CPAREN, `")"`)
474 if err != nil {
475 return nil, err
476 }
477
478 return &ast.Call{
479 Func: funcName,
480 Args: args,
481 Posx: startPos,
482 }, nil
483 }
484
485 varNode := &ast.VariableAccess{
486 Name: first.Content,
487 Posx: startPos,
488 }
489
490 if p.peeker.Peek().Type == scanner.OBRACKET {
491 // index operator
492 startPos := p.peeker.Read().Pos // eat bracket
493 indexExpr, err := p.ParseExpression()
494 if err != nil {
495 return nil, err
496 }
497 err = p.requireTokenType(scanner.CBRACKET, `"]"`)
498 if err != nil {
499 return nil, err
500 }
501 return &ast.Index{
502 Target: varNode,
503 Key: indexExpr,
504 Posx: startPos,
505 }, nil
506 }
507
508 return varNode, nil
509}
510
511// requireTokenType consumes the next token an returns an error if its
512// type does not match the given type. nil is returned if the type matches.
513//
514// This is a helper around peeker.Read() for situations where the parser just
515// wants to assert that a particular token type must be present.
516func (p *parser) requireTokenType(wantType scanner.TokenType, wantName string) error {
517 token := p.peeker.Read()
518 if token.Type != wantType {
519 return ExpectationError(wantName, token)
520 }
521 return nil
522}
diff --git a/vendor/github.com/hashicorp/hil/scanner/peeker.go b/vendor/github.com/hashicorp/hil/scanner/peeker.go
new file mode 100644
index 0000000..4de3728
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/scanner/peeker.go
@@ -0,0 +1,55 @@
1package scanner
2
3// Peeker is a utility that wraps a token channel returned by Scan and
4// provides an interface that allows a caller (e.g. the parser) to
5// work with the token stream in a mode that allows one token of lookahead,
6// and provides utilities for more convenient processing of the stream.
7type Peeker struct {
8 ch <-chan *Token
9 peeked *Token
10}
11
12func NewPeeker(ch <-chan *Token) *Peeker {
13 return &Peeker{
14 ch: ch,
15 }
16}
17
18// Peek returns the next token in the stream without consuming it. A
19// subsequent call to Read will return the same token.
20func (p *Peeker) Peek() *Token {
21 if p.peeked == nil {
22 p.peeked = <-p.ch
23 }
24 return p.peeked
25}
26
27// Read consumes the next token in the stream and returns it.
28func (p *Peeker) Read() *Token {
29 token := p.Peek()
30
31 // As a special case, we will produce the EOF token forever once
32 // it is reached.
33 if token.Type != EOF {
34 p.peeked = nil
35 }
36
37 return token
38}
39
40// Close ensures that the token stream has been exhausted, to prevent
41// the goroutine in the underlying scanner from leaking.
42//
43// It's not necessary to call this if the caller reads the token stream
44// to EOF, since that implicitly closes the scanner.
45func (p *Peeker) Close() {
46 for _ = range p.ch {
47 // discard
48 }
49 // Install a synthetic EOF token in 'peeked' in case someone
50 // erroneously calls Peek() or Read() after we've closed.
51 p.peeked = &Token{
52 Type: EOF,
53 Content: "",
54 }
55}
diff --git a/vendor/github.com/hashicorp/hil/scanner/scanner.go b/vendor/github.com/hashicorp/hil/scanner/scanner.go
new file mode 100644
index 0000000..bab86c6
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/scanner/scanner.go
@@ -0,0 +1,550 @@
1package scanner
2
3import (
4 "unicode"
5 "unicode/utf8"
6
7 "github.com/hashicorp/hil/ast"
8)
9
10// Scan returns a channel that recieves Tokens from the given input string.
11//
12// The scanner's job is just to partition the string into meaningful parts.
13// It doesn't do any transformation of the raw input string, so the caller
14// must deal with any further interpretation required, such as parsing INTEGER
15// tokens into real ints, or dealing with escape sequences in LITERAL or
16// STRING tokens.
17//
18// Strings in the returned tokens are slices from the original string.
19//
20// startPos should be set to ast.InitPos unless the caller knows that
21// this interpolation string is part of a larger file and knows the position
22// of the first character in that larger file.
23func Scan(s string, startPos ast.Pos) <-chan *Token {
24 ch := make(chan *Token)
25 go scan(s, ch, startPos)
26 return ch
27}
28
29func scan(s string, ch chan<- *Token, pos ast.Pos) {
30 // 'remain' starts off as the whole string but we gradually
31 // slice of the front of it as we work our way through.
32 remain := s
33
34 // nesting keeps track of how many ${ .. } sequences we are
35 // inside, so we can recognize the minor differences in syntax
36 // between outer string literals (LITERAL tokens) and quoted
37 // string literals (STRING tokens).
38 nesting := 0
39
40 // We're going to flip back and forth between parsing literals/strings
41 // and parsing interpolation sequences ${ .. } until we reach EOF or
42 // some INVALID token.
43All:
44 for {
45 startPos := pos
46 // Literal string processing first, since the beginning of
47 // a string is always outside of an interpolation sequence.
48 literalVal, terminator := scanLiteral(remain, pos, nesting > 0)
49
50 if len(literalVal) > 0 {
51 litType := LITERAL
52 if nesting > 0 {
53 litType = STRING
54 }
55 ch <- &Token{
56 Type: litType,
57 Content: literalVal,
58 Pos: startPos,
59 }
60 remain = remain[len(literalVal):]
61 }
62
63 ch <- terminator
64 remain = remain[len(terminator.Content):]
65 pos = terminator.Pos
66 // Safe to use len() here because none of the terminator tokens
67 // can contain UTF-8 sequences.
68 pos.Column = pos.Column + len(terminator.Content)
69
70 switch terminator.Type {
71 case INVALID:
72 // Synthetic EOF after invalid token, since further scanning
73 // is likely to just produce more garbage.
74 ch <- &Token{
75 Type: EOF,
76 Content: "",
77 Pos: pos,
78 }
79 break All
80 case EOF:
81 // All done!
82 break All
83 case BEGIN:
84 nesting++
85 case CQUOTE:
86 // nothing special to do
87 default:
88 // Should never happen
89 panic("invalid string/literal terminator")
90 }
91
92 // Now we do the processing of the insides of ${ .. } sequences.
93 // This loop terminates when we encounter either a closing } or
94 // an opening ", which will cause us to return to literal processing.
95 Interpolation:
96 for {
97
98 token, size, newPos := scanInterpolationToken(remain, pos)
99 ch <- token
100 remain = remain[size:]
101 pos = newPos
102
103 switch token.Type {
104 case INVALID:
105 // Synthetic EOF after invalid token, since further scanning
106 // is likely to just produce more garbage.
107 ch <- &Token{
108 Type: EOF,
109 Content: "",
110 Pos: pos,
111 }
112 break All
113 case EOF:
114 // All done
115 // (though a syntax error that we'll catch in the parser)
116 break All
117 case END:
118 nesting--
119 if nesting < 0 {
120 // Can happen if there are unbalanced ${ and } sequences
121 // in the input, which we'll catch in the parser.
122 nesting = 0
123 }
124 break Interpolation
125 case OQUOTE:
126 // Beginning of nested quoted string
127 break Interpolation
128 }
129 }
130 }
131
132 close(ch)
133}
134
135// Returns the token found at the start of the given string, followed by
136// the number of bytes that were consumed from the string and the adjusted
137// source position.
138//
139// Note that the number of bytes consumed can be more than the length of
140// the returned token contents if the string begins with whitespace, since
141// it will be silently consumed before reading the token.
142func scanInterpolationToken(s string, startPos ast.Pos) (*Token, int, ast.Pos) {
143 pos := startPos
144 size := 0
145
146 // Consume whitespace, if any
147 for len(s) > 0 && byteIsSpace(s[0]) {
148 if s[0] == '\n' {
149 pos.Column = 1
150 pos.Line++
151 } else {
152 pos.Column++
153 }
154 size++
155 s = s[1:]
156 }
157
158 // Unexpected EOF during sequence
159 if len(s) == 0 {
160 return &Token{
161 Type: EOF,
162 Content: "",
163 Pos: pos,
164 }, size, pos
165 }
166
167 next := s[0]
168 var token *Token
169
170 switch next {
171 case '(', ')', '[', ']', ',', '.', '+', '-', '*', '/', '%', '?', ':':
172 // Easy punctuation symbols that don't have any special meaning
173 // during scanning, and that stand for themselves in the
174 // TokenType enumeration.
175 token = &Token{
176 Type: TokenType(next),
177 Content: s[:1],
178 Pos: pos,
179 }
180 case '}':
181 token = &Token{
182 Type: END,
183 Content: s[:1],
184 Pos: pos,
185 }
186 case '"':
187 token = &Token{
188 Type: OQUOTE,
189 Content: s[:1],
190 Pos: pos,
191 }
192 case '!':
193 if len(s) >= 2 && s[:2] == "!=" {
194 token = &Token{
195 Type: NOTEQUAL,
196 Content: s[:2],
197 Pos: pos,
198 }
199 } else {
200 token = &Token{
201 Type: BANG,
202 Content: s[:1],
203 Pos: pos,
204 }
205 }
206 case '<':
207 if len(s) >= 2 && s[:2] == "<=" {
208 token = &Token{
209 Type: LTE,
210 Content: s[:2],
211 Pos: pos,
212 }
213 } else {
214 token = &Token{
215 Type: LT,
216 Content: s[:1],
217 Pos: pos,
218 }
219 }
220 case '>':
221 if len(s) >= 2 && s[:2] == ">=" {
222 token = &Token{
223 Type: GTE,
224 Content: s[:2],
225 Pos: pos,
226 }
227 } else {
228 token = &Token{
229 Type: GT,
230 Content: s[:1],
231 Pos: pos,
232 }
233 }
234 case '=':
235 if len(s) >= 2 && s[:2] == "==" {
236 token = &Token{
237 Type: EQUAL,
238 Content: s[:2],
239 Pos: pos,
240 }
241 } else {
242 // A single equals is not a valid operator
243 token = &Token{
244 Type: INVALID,
245 Content: s[:1],
246 Pos: pos,
247 }
248 }
249 case '&':
250 if len(s) >= 2 && s[:2] == "&&" {
251 token = &Token{
252 Type: AND,
253 Content: s[:2],
254 Pos: pos,
255 }
256 } else {
257 token = &Token{
258 Type: INVALID,
259 Content: s[:1],
260 Pos: pos,
261 }
262 }
263 case '|':
264 if len(s) >= 2 && s[:2] == "||" {
265 token = &Token{
266 Type: OR,
267 Content: s[:2],
268 Pos: pos,
269 }
270 } else {
271 token = &Token{
272 Type: INVALID,
273 Content: s[:1],
274 Pos: pos,
275 }
276 }
277 default:
278 if next >= '0' && next <= '9' {
279 num, numType := scanNumber(s)
280 token = &Token{
281 Type: numType,
282 Content: num,
283 Pos: pos,
284 }
285 } else if stringStartsWithIdentifier(s) {
286 ident, runeLen := scanIdentifier(s)
287 tokenType := IDENTIFIER
288 if ident == "true" || ident == "false" {
289 tokenType = BOOL
290 }
291 token = &Token{
292 Type: tokenType,
293 Content: ident,
294 Pos: pos,
295 }
296 // Skip usual token handling because it doesn't
297 // know how to deal with UTF-8 sequences.
298 pos.Column = pos.Column + runeLen
299 return token, size + len(ident), pos
300 } else {
301 _, byteLen := utf8.DecodeRuneInString(s)
302 token = &Token{
303 Type: INVALID,
304 Content: s[:byteLen],
305 Pos: pos,
306 }
307 // Skip usual token handling because it doesn't
308 // know how to deal with UTF-8 sequences.
309 pos.Column = pos.Column + 1
310 return token, size + byteLen, pos
311 }
312 }
313
314 // Here we assume that the token content contains no UTF-8 sequences,
315 // because we dealt with UTF-8 characters as a special case where
316 // necessary above.
317 size = size + len(token.Content)
318 pos.Column = pos.Column + len(token.Content)
319
320 return token, size, pos
321}
322
323// Returns the (possibly-empty) prefix of the given string that represents
324// a literal, followed by the token that marks the end of the literal.
325func scanLiteral(s string, startPos ast.Pos, nested bool) (string, *Token) {
326 litLen := 0
327 pos := startPos
328 var terminator *Token
329 for {
330
331 if litLen >= len(s) {
332 if nested {
333 // We've ended in the middle of a quoted string,
334 // which means this token is actually invalid.
335 return "", &Token{
336 Type: INVALID,
337 Content: s,
338 Pos: startPos,
339 }
340 }
341 terminator = &Token{
342 Type: EOF,
343 Content: "",
344 Pos: pos,
345 }
346 break
347 }
348
349 next := s[litLen]
350
351 if next == '$' && len(s) > litLen+1 {
352 follow := s[litLen+1]
353
354 if follow == '{' {
355 terminator = &Token{
356 Type: BEGIN,
357 Content: s[litLen : litLen+2],
358 Pos: pos,
359 }
360 pos.Column = pos.Column + 2
361 break
362 } else if follow == '$' {
363 // Double-$ escapes the special processing of $,
364 // so we will consume both characters here.
365 pos.Column = pos.Column + 2
366 litLen = litLen + 2
367 continue
368 }
369 }
370
371 // special handling that applies only to quoted strings
372 if nested {
373 if next == '"' {
374 terminator = &Token{
375 Type: CQUOTE,
376 Content: s[litLen : litLen+1],
377 Pos: pos,
378 }
379 pos.Column = pos.Column + 1
380 break
381 }
382
383 // Escaped quote marks do not terminate the string.
384 //
385 // All we do here in the scanner is avoid terminating a string
386 // due to an escaped quote. The parser is responsible for the
387 // full handling of escape sequences, since it's able to produce
388 // better error messages than we can produce in here.
389 if next == '\\' && len(s) > litLen+1 {
390 follow := s[litLen+1]
391
392 if follow == '"' {
393 // \" escapes the special processing of ",
394 // so we will consume both characters here.
395 pos.Column = pos.Column + 2
396 litLen = litLen + 2
397 continue
398 }
399 }
400 }
401
402 if next == '\n' {
403 pos.Column = 1
404 pos.Line++
405 litLen++
406 } else {
407 pos.Column++
408
409 // "Column" measures runes, so we need to actually consume
410 // a valid UTF-8 character here.
411 _, size := utf8.DecodeRuneInString(s[litLen:])
412 litLen = litLen + size
413 }
414
415 }
416
417 return s[:litLen], terminator
418}
419
420// scanNumber returns the extent of the prefix of the string that represents
421// a valid number, along with what type of number it represents: INT or FLOAT.
422//
423// scanNumber does only basic character analysis: numbers consist of digits
424// and periods, with at least one period signalling a FLOAT. It's the parser's
425// responsibility to validate the form and range of the number, such as ensuring
426// that a FLOAT actually contains only one period, etc.
427func scanNumber(s string) (string, TokenType) {
428 period := -1
429 byteLen := 0
430 numType := INTEGER
431 for {
432 if byteLen >= len(s) {
433 break
434 }
435
436 next := s[byteLen]
437 if next != '.' && (next < '0' || next > '9') {
438 // If our last value was a period, then we're not a float,
439 // we're just an integer that ends in a period.
440 if period == byteLen-1 {
441 byteLen--
442 numType = INTEGER
443 }
444
445 break
446 }
447
448 if next == '.' {
449 // If we've already seen a period, break out
450 if period >= 0 {
451 break
452 }
453
454 period = byteLen
455 numType = FLOAT
456 }
457
458 byteLen++
459 }
460
461 return s[:byteLen], numType
462}
463
464// scanIdentifier returns the extent of the prefix of the string that
465// represents a valid identifier, along with the length of that prefix
466// in runes.
467//
468// Identifiers may contain utf8-encoded non-Latin letters, which will
469// cause the returned "rune length" to be shorter than the byte length
470// of the returned string.
471func scanIdentifier(s string) (string, int) {
472 byteLen := 0
473 runeLen := 0
474 for {
475 if byteLen >= len(s) {
476 break
477 }
478
479 nextRune, size := utf8.DecodeRuneInString(s[byteLen:])
480 if !(nextRune == '_' ||
481 nextRune == '-' ||
482 nextRune == '.' ||
483 nextRune == '*' ||
484 unicode.IsNumber(nextRune) ||
485 unicode.IsLetter(nextRune) ||
486 unicode.IsMark(nextRune)) {
487 break
488 }
489
490 // If we reach a star, it must be between periods to be part
491 // of the same identifier.
492 if nextRune == '*' && s[byteLen-1] != '.' {
493 break
494 }
495
496 // If our previous character was a star, then the current must
497 // be period. Otherwise, undo that and exit.
498 if byteLen > 0 && s[byteLen-1] == '*' && nextRune != '.' {
499 byteLen--
500 if s[byteLen-1] == '.' {
501 byteLen--
502 }
503
504 break
505 }
506
507 byteLen = byteLen + size
508 runeLen = runeLen + 1
509 }
510
511 return s[:byteLen], runeLen
512}
513
514// byteIsSpace implements a restrictive interpretation of spaces that includes
515// only what's valid inside interpolation sequences: spaces, tabs, newlines.
516func byteIsSpace(b byte) bool {
517 switch b {
518 case ' ', '\t', '\r', '\n':
519 return true
520 default:
521 return false
522 }
523}
524
525// stringStartsWithIdentifier returns true if the given string begins with
526// a character that is a legal start of an identifier: an underscore or
527// any character that Unicode considers to be a letter.
528func stringStartsWithIdentifier(s string) bool {
529 if len(s) == 0 {
530 return false
531 }
532
533 first := s[0]
534
535 // Easy ASCII cases first
536 if (first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z') || first == '_' {
537 return true
538 }
539
540 // If our first byte begins a UTF-8 sequence then the sequence might
541 // be a unicode letter.
542 if utf8.RuneStart(first) {
543 firstRune, _ := utf8.DecodeRuneInString(s)
544 if unicode.IsLetter(firstRune) {
545 return true
546 }
547 }
548
549 return false
550}
diff --git a/vendor/github.com/hashicorp/hil/scanner/token.go b/vendor/github.com/hashicorp/hil/scanner/token.go
new file mode 100644
index 0000000..b6c82ae
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/scanner/token.go
@@ -0,0 +1,105 @@
1package scanner
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/hil/ast"
7)
8
9type Token struct {
10 Type TokenType
11 Content string
12 Pos ast.Pos
13}
14
15//go:generate stringer -type=TokenType
16type TokenType rune
17
18const (
19 // Raw string data outside of ${ .. } sequences
20 LITERAL TokenType = 'o'
21
22 // STRING is like a LITERAL but it's inside a quoted string
23 // within a ${ ... } sequence, and so it can contain backslash
24 // escaping.
25 STRING TokenType = 'S'
26
27 // Other Literals
28 INTEGER TokenType = 'I'
29 FLOAT TokenType = 'F'
30 BOOL TokenType = 'B'
31
32 BEGIN TokenType = '$' // actually "${"
33 END TokenType = '}'
34 OQUOTE TokenType = '“' // Opening quote of a nested quoted sequence
35 CQUOTE TokenType = '”' // Closing quote of a nested quoted sequence
36 OPAREN TokenType = '('
37 CPAREN TokenType = ')'
38 OBRACKET TokenType = '['
39 CBRACKET TokenType = ']'
40 COMMA TokenType = ','
41
42 IDENTIFIER TokenType = 'i'
43
44 PERIOD TokenType = '.'
45 PLUS TokenType = '+'
46 MINUS TokenType = '-'
47 STAR TokenType = '*'
48 SLASH TokenType = '/'
49 PERCENT TokenType = '%'
50
51 AND TokenType = '∧'
52 OR TokenType = '∨'
53 BANG TokenType = '!'
54
55 EQUAL TokenType = '='
56 NOTEQUAL TokenType = '≠'
57 GT TokenType = '>'
58 LT TokenType = '<'
59 GTE TokenType = '≥'
60 LTE TokenType = '≤'
61
62 QUESTION TokenType = '?'
63 COLON TokenType = ':'
64
65 EOF TokenType = '␄'
66
67 // Produced for sequences that cannot be understood as valid tokens
68 // e.g. due to use of unrecognized punctuation.
69 INVALID TokenType = '�'
70)
71
72func (t *Token) String() string {
73 switch t.Type {
74 case EOF:
75 return "end of string"
76 case INVALID:
77 return fmt.Sprintf("invalid sequence %q", t.Content)
78 case INTEGER:
79 return fmt.Sprintf("integer %s", t.Content)
80 case FLOAT:
81 return fmt.Sprintf("float %s", t.Content)
82 case STRING:
83 return fmt.Sprintf("string %q", t.Content)
84 case LITERAL:
85 return fmt.Sprintf("literal %q", t.Content)
86 case OQUOTE:
87 return fmt.Sprintf("opening quote")
88 case CQUOTE:
89 return fmt.Sprintf("closing quote")
90 case AND:
91 return "&&"
92 case OR:
93 return "||"
94 case NOTEQUAL:
95 return "!="
96 case GTE:
97 return ">="
98 case LTE:
99 return "<="
100 default:
101 // The remaining token types have content that
102 // speaks for itself.
103 return fmt.Sprintf("%q", t.Content)
104 }
105}
diff --git a/vendor/github.com/hashicorp/hil/scanner/tokentype_string.go b/vendor/github.com/hashicorp/hil/scanner/tokentype_string.go
new file mode 100644
index 0000000..a602f5f
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/scanner/tokentype_string.go
@@ -0,0 +1,51 @@
1// Code generated by "stringer -type=TokenType"; DO NOT EDIT
2
3package scanner
4
5import "fmt"
6
7const _TokenType_name = "BANGBEGINPERCENTOPARENCPARENSTARPLUSCOMMAMINUSPERIODSLASHCOLONLTEQUALGTQUESTIONBOOLFLOATINTEGERSTRINGOBRACKETCBRACKETIDENTIFIERLITERALENDOQUOTECQUOTEANDORNOTEQUALLTEGTEEOFINVALID"
8
9var _TokenType_map = map[TokenType]string{
10 33: _TokenType_name[0:4],
11 36: _TokenType_name[4:9],
12 37: _TokenType_name[9:16],
13 40: _TokenType_name[16:22],
14 41: _TokenType_name[22:28],
15 42: _TokenType_name[28:32],
16 43: _TokenType_name[32:36],
17 44: _TokenType_name[36:41],
18 45: _TokenType_name[41:46],
19 46: _TokenType_name[46:52],
20 47: _TokenType_name[52:57],
21 58: _TokenType_name[57:62],
22 60: _TokenType_name[62:64],
23 61: _TokenType_name[64:69],
24 62: _TokenType_name[69:71],
25 63: _TokenType_name[71:79],
26 66: _TokenType_name[79:83],
27 70: _TokenType_name[83:88],
28 73: _TokenType_name[88:95],
29 83: _TokenType_name[95:101],
30 91: _TokenType_name[101:109],
31 93: _TokenType_name[109:117],
32 105: _TokenType_name[117:127],
33 111: _TokenType_name[127:134],
34 125: _TokenType_name[134:137],
35 8220: _TokenType_name[137:143],
36 8221: _TokenType_name[143:149],
37 8743: _TokenType_name[149:152],
38 8744: _TokenType_name[152:154],
39 8800: _TokenType_name[154:162],
40 8804: _TokenType_name[162:165],
41 8805: _TokenType_name[165:168],
42 9220: _TokenType_name[168:171],
43 65533: _TokenType_name[171:178],
44}
45
46func (i TokenType) String() string {
47 if str, ok := _TokenType_map[i]; ok {
48 return str
49 }
50 return fmt.Sprintf("TokenType(%d)", i)
51}
diff --git a/vendor/github.com/hashicorp/hil/transform_fixed.go b/vendor/github.com/hashicorp/hil/transform_fixed.go
new file mode 100644
index 0000000..e69df29
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/transform_fixed.go
@@ -0,0 +1,29 @@
1package hil
2
3import (
4 "github.com/hashicorp/hil/ast"
5)
6
7// FixedValueTransform transforms an AST to return a fixed value for
8// all interpolations. i.e. you can make "hi ${anything}" always
9// turn into "hi foo".
10//
11// The primary use case for this is for config validations where you can
12// verify that interpolations result in a certain type of string.
13func FixedValueTransform(root ast.Node, Value *ast.LiteralNode) ast.Node {
14 // We visit the nodes in top-down order
15 result := root
16 switch n := result.(type) {
17 case *ast.Output:
18 for i, v := range n.Exprs {
19 n.Exprs[i] = FixedValueTransform(v, Value)
20 }
21 case *ast.LiteralNode:
22 // We keep it as-is
23 default:
24 // Anything else we replace
25 result = Value
26 }
27
28 return result
29}
diff --git a/vendor/github.com/hashicorp/hil/walk.go b/vendor/github.com/hashicorp/hil/walk.go
new file mode 100644
index 0000000..0ace830
--- /dev/null
+++ b/vendor/github.com/hashicorp/hil/walk.go
@@ -0,0 +1,266 @@
1package hil
2
3import (
4 "fmt"
5 "reflect"
6 "strings"
7
8 "github.com/hashicorp/hil/ast"
9 "github.com/mitchellh/reflectwalk"
10)
11
12// WalkFn is the type of function to pass to Walk. Modify fields within
13// WalkData to control whether replacement happens.
14type WalkFn func(*WalkData) error
15
16// WalkData is the structure passed to the callback of the Walk function.
17//
18// This structure contains data passed in as well as fields that are expected
19// to be written by the caller as a result. Please see the documentation for
20// each field for more information.
21type WalkData struct {
22 // Root is the parsed root of this HIL program
23 Root ast.Node
24
25 // Location is the location within the structure where this
26 // value was found. This can be used to modify behavior within
27 // slices and so on.
28 Location reflectwalk.Location
29
30 // The below two values must be set by the callback to have any effect.
31 //
32 // Replace, if true, will replace the value in the structure with
33 // ReplaceValue. It is up to the caller to make sure this is a string.
34 Replace bool
35 ReplaceValue string
36}
37
38// Walk will walk an arbitrary Go structure and parse any string as an
39// HIL program and call the callback cb to determine what to replace it
40// with.
41//
42// This function is very useful for arbitrary HIL program interpolation
43// across a complex configuration structure. Due to the heavy use of
44// reflection in this function, it is recommend to write many unit tests
45// with your typical configuration structures to hilp mitigate the risk
46// of panics.
47func Walk(v interface{}, cb WalkFn) error {
48 walker := &interpolationWalker{F: cb}
49 return reflectwalk.Walk(v, walker)
50}
51
52// interpolationWalker implements interfaces for the reflectwalk package
53// (github.com/mitchellh/reflectwalk) that can be used to automatically
54// execute a callback for an interpolation.
55type interpolationWalker struct {
56 F WalkFn
57
58 key []string
59 lastValue reflect.Value
60 loc reflectwalk.Location
61 cs []reflect.Value
62 csKey []reflect.Value
63 csData interface{}
64 sliceIndex int
65 unknownKeys []string
66}
67
68func (w *interpolationWalker) Enter(loc reflectwalk.Location) error {
69 w.loc = loc
70 return nil
71}
72
73func (w *interpolationWalker) Exit(loc reflectwalk.Location) error {
74 w.loc = reflectwalk.None
75
76 switch loc {
77 case reflectwalk.Map:
78 w.cs = w.cs[:len(w.cs)-1]
79 case reflectwalk.MapValue:
80 w.key = w.key[:len(w.key)-1]
81 w.csKey = w.csKey[:len(w.csKey)-1]
82 case reflectwalk.Slice:
83 // Split any values that need to be split
84 w.splitSlice()
85 w.cs = w.cs[:len(w.cs)-1]
86 case reflectwalk.SliceElem:
87 w.csKey = w.csKey[:len(w.csKey)-1]
88 }
89
90 return nil
91}
92
93func (w *interpolationWalker) Map(m reflect.Value) error {
94 w.cs = append(w.cs, m)
95 return nil
96}
97
98func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error {
99 w.csData = k
100 w.csKey = append(w.csKey, k)
101 w.key = append(w.key, k.String())
102 w.lastValue = v
103 return nil
104}
105
106func (w *interpolationWalker) Slice(s reflect.Value) error {
107 w.cs = append(w.cs, s)
108 return nil
109}
110
111func (w *interpolationWalker) SliceElem(i int, elem reflect.Value) error {
112 w.csKey = append(w.csKey, reflect.ValueOf(i))
113 w.sliceIndex = i
114 return nil
115}
116
117func (w *interpolationWalker) Primitive(v reflect.Value) error {
118 setV := v
119
120 // We only care about strings
121 if v.Kind() == reflect.Interface {
122 setV = v
123 v = v.Elem()
124 }
125 if v.Kind() != reflect.String {
126 return nil
127 }
128
129 astRoot, err := Parse(v.String())
130 if err != nil {
131 return err
132 }
133
134 // If the AST we got is just a literal string value with the same
135 // value then we ignore it. We have to check if its the same value
136 // because it is possible to input a string, get out a string, and
137 // have it be different. For example: "foo-$${bar}" turns into
138 // "foo-${bar}"
139 if n, ok := astRoot.(*ast.LiteralNode); ok {
140 if s, ok := n.Value.(string); ok && s == v.String() {
141 return nil
142 }
143 }
144
145 if w.F == nil {
146 return nil
147 }
148
149 data := WalkData{Root: astRoot, Location: w.loc}
150 if err := w.F(&data); err != nil {
151 return fmt.Errorf(
152 "%s in:\n\n%s",
153 err, v.String())
154 }
155
156 if data.Replace {
157 /*
158 if remove {
159 w.removeCurrent()
160 return nil
161 }
162 */
163
164 resultVal := reflect.ValueOf(data.ReplaceValue)
165 switch w.loc {
166 case reflectwalk.MapKey:
167 m := w.cs[len(w.cs)-1]
168
169 // Delete the old value
170 var zero reflect.Value
171 m.SetMapIndex(w.csData.(reflect.Value), zero)
172
173 // Set the new key with the existing value
174 m.SetMapIndex(resultVal, w.lastValue)
175
176 // Set the key to be the new key
177 w.csData = resultVal
178 case reflectwalk.MapValue:
179 // If we're in a map, then the only way to set a map value is
180 // to set it directly.
181 m := w.cs[len(w.cs)-1]
182 mk := w.csData.(reflect.Value)
183 m.SetMapIndex(mk, resultVal)
184 default:
185 // Otherwise, we should be addressable
186 setV.Set(resultVal)
187 }
188 }
189
190 return nil
191}
192
193func (w *interpolationWalker) removeCurrent() {
194 // Append the key to the unknown keys
195 w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, "."))
196
197 for i := 1; i <= len(w.cs); i++ {
198 c := w.cs[len(w.cs)-i]
199 switch c.Kind() {
200 case reflect.Map:
201 // Zero value so that we delete the map key
202 var val reflect.Value
203
204 // Get the key and delete it
205 k := w.csData.(reflect.Value)
206 c.SetMapIndex(k, val)
207 return
208 }
209 }
210
211 panic("No container found for removeCurrent")
212}
213
214func (w *interpolationWalker) replaceCurrent(v reflect.Value) {
215 c := w.cs[len(w.cs)-2]
216 switch c.Kind() {
217 case reflect.Map:
218 // Get the key and delete it
219 k := w.csKey[len(w.csKey)-1]
220 c.SetMapIndex(k, v)
221 }
222}
223
224func (w *interpolationWalker) splitSlice() {
225 // Get the []interface{} slice so we can do some operations on
226 // it without dealing with reflection. We'll document each step
227 // here to be clear.
228 var s []interface{}
229 raw := w.cs[len(w.cs)-1]
230 switch v := raw.Interface().(type) {
231 case []interface{}:
232 s = v
233 case []map[string]interface{}:
234 return
235 default:
236 panic("Unknown kind: " + raw.Kind().String())
237 }
238
239 // Check if we have any elements that we need to split. If not, then
240 // just return since we're done.
241 split := false
242 if !split {
243 return
244 }
245
246 // Make a new result slice that is twice the capacity to fit our growth.
247 result := make([]interface{}, 0, len(s)*2)
248
249 // Go over each element of the original slice and start building up
250 // the resulting slice by splitting where we have to.
251 for _, v := range s {
252 sv, ok := v.(string)
253 if !ok {
254 // Not a string, so just set it
255 result = append(result, v)
256 continue
257 }
258
259 // Not a string list, so just set it
260 result = append(result, sv)
261 }
262
263 // Our slice is now done, we have to replace the slice now
264 // with this new one that we have.
265 w.replaceCurrent(reflect.ValueOf(result))
266}