diff options
author | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
---|---|---|
committer | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
commit | bae9f6d2fd5eb5bc80929bd393932b23f14d7c93 (patch) | |
tree | ca9ab12a7d78b1fc27a8f734729081357ce6d252 /vendor/github.com/hashicorp/hil | |
parent | 254c495b6bebab3fb72a243c4bce858d79e6ee99 (diff) | |
download | terraform-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')
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 @@ | |||
1 | Mozilla Public License, version 2.0 | ||
2 | |||
3 | 1. Definitions | ||
4 | |||
5 | 1.1. “Contributor” | ||
6 | |||
7 | means each individual or legal entity that creates, contributes to the | ||
8 | creation of, or owns Covered Software. | ||
9 | |||
10 | 1.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 | |||
15 | 1.3. “Contribution” | ||
16 | |||
17 | means Covered Software of a particular Contributor. | ||
18 | |||
19 | 1.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 | |||
26 | 1.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 | |||
36 | 1.6. “Executable Form” | ||
37 | |||
38 | means any form of the work other than Source Code Form. | ||
39 | |||
40 | 1.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 | |||
45 | 1.8. “License” | ||
46 | |||
47 | means this document. | ||
48 | |||
49 | 1.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 | |||
55 | 1.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 | |||
64 | 1.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 | |||
72 | 1.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 | |||
78 | 1.13. “Source Code Form” | ||
79 | |||
80 | means the form of the work preferred for making modifications. | ||
81 | |||
82 | 1.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 | |||
93 | 2. License Grants and Conditions | ||
94 | |||
95 | 2.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 | |||
110 | 2.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 | |||
116 | 2.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 | |||
137 | 2.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 | |||
144 | 2.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 | |||
150 | 2.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 | |||
155 | 2.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 | |||
161 | 3. Responsibilities | ||
162 | |||
163 | 3.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 | |||
172 | 3.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 | |||
187 | 3.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 | |||
199 | 3.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 | |||
207 | 3.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 | |||
220 | 4. 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 | |||
232 | 5. Termination | ||
233 | |||
234 | 5.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 | |||
247 | 5.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 | |||
254 | 5.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 | |||
259 | 6. 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 | |||
272 | 7. 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 | |||
287 | 8. 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 | |||
295 | 9. 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 | |||
305 | 10. Versions of the License | ||
306 | |||
307 | 10.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 | |||
314 | 10.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 | |||
321 | 10.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 | |||
329 | 10.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 | |||
334 | Exhibit 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 | |||
343 | If it is not possible or desirable to put the notice in a particular file, then | ||
344 | You may include the notice in a location (such as a LICENSE file in a relevant | ||
345 | directory) where a recipient would be likely to look for such a notice. | ||
346 | |||
347 | You may add additional accurate notices of copyright ownership. | ||
348 | |||
349 | Exhibit 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 | |||
5 | HIL (HashiCorp Interpolation Language) is a lightweight embedded language used | ||
6 | primarily for configuration interpolation. The goal of HIL is to make a simple | ||
7 | language for interpolations in the various configurations of HashiCorp tools. | ||
8 | |||
9 | HIL is built to interpolate any string, but is in use by HashiCorp primarily | ||
10 | with [HCL](https://github.com/hashicorp/hcl). HCL is _not required_ in any | ||
11 | way for use with HIL. | ||
12 | |||
13 | HIL isn't meant to be a general purpose language. It was built for basic | ||
14 | configuration interpolations. Therefore, you can't currently write functions, | ||
15 | have conditionals, set intermediary variables, etc. within HIL itself. It is | ||
16 | possible some of these may be added later but the right use case must exist. | ||
17 | |||
18 | ## Why? | ||
19 | |||
20 | Many of our tools have support for something similar to templates, but | ||
21 | within the configuration itself. The most prominent requirement was in | ||
22 | [Terraform](https://github.com/hashicorp/terraform) where we wanted the | ||
23 | configuration to be able to reference values from elsewhere in the | ||
24 | configuration. Example: | ||
25 | |||
26 | foo = "hi ${var.world}" | ||
27 | |||
28 | We originally used a full templating language for this, but found it | ||
29 | was too heavy weight. Additionally, many full languages required bindings | ||
30 | to C (and thus the usage of cgo) which we try to avoid to make cross-compilation | ||
31 | easier. We then moved to very basic regular expression based | ||
32 | string replacement, but found the need for basic arithmetic and function | ||
33 | calls resulting in overly complex regular expressions. | ||
34 | |||
35 | Ultimately, we wrote our own mini-language within Terraform itself. As | ||
36 | we built other projects such as [Nomad](https://nomadproject.io) and | ||
37 | [Otto](https://ottoproject.io), the need for basic interpolations arose | ||
38 | again. | ||
39 | |||
40 | Thus HIL was born. It is extracted from Terraform, cleaned up, and | ||
41 | better tested for general purpose use. | ||
42 | |||
43 | ## Syntax | ||
44 | |||
45 | For a complete grammar, please see the parser itself. A high-level overview | ||
46 | of the syntax and grammer is listed here. | ||
47 | |||
48 | Code begins within `${` and `}`. Outside of this, text is treated | ||
49 | literally. For example, `foo` is a valid HIL program that is just the | ||
50 | string "foo", but `foo ${bar}` is an HIL program that is the string "foo " | ||
51 | concatened with the value of `bar`. For the remainder of the syntax | ||
52 | docs, 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 | |||
82 | We've used this mini-language in Terraform for years. For backwards compatibility | ||
83 | reasons, we're unlikely to make an incompatible change to the language but | ||
84 | we're not currently making that promise, either. | ||
85 | |||
86 | The internal API of this project may very well change as we evolve it | ||
87 | to work with more of our projects. We recommend using some sort of dependency | ||
88 | management solution with this package. | ||
89 | |||
90 | ## Future Changes | ||
91 | |||
92 | The 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 @@ | |||
1 | version: "build-{branch}-{build}" | ||
2 | image: Visual Studio 2015 | ||
3 | clone_folder: c:\gopath\src\github.com\hashicorp\hil | ||
4 | environment: | ||
5 | GOPATH: c:\gopath | ||
6 | init: | ||
7 | - git config --global core.autocrlf true | ||
8 | install: | ||
9 | - cmd: >- | ||
10 | echo %Path% | ||
11 | |||
12 | go version | ||
13 | |||
14 | go env | ||
15 | |||
16 | go get -d -v -t ./... | ||
17 | build_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 @@ | |||
1 | package ast | ||
2 | |||
3 | import ( | ||
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. | ||
10 | type Arithmetic struct { | ||
11 | Op ArithmeticOp | ||
12 | Exprs []Node | ||
13 | Posx Pos | ||
14 | } | ||
15 | |||
16 | func (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 | |||
24 | func (n *Arithmetic) Pos() Pos { | ||
25 | return n.Posx | ||
26 | } | ||
27 | |||
28 | func (n *Arithmetic) GoString() string { | ||
29 | return fmt.Sprintf("*%#v", *n) | ||
30 | } | ||
31 | |||
32 | func (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 | |||
41 | func (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 @@ | |||
1 | package ast | ||
2 | |||
3 | // ArithmeticOp is the operation to use for the math. | ||
4 | type ArithmeticOp int | ||
5 | |||
6 | const ( | ||
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 @@ | |||
1 | package ast | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | ) | ||
6 | |||
7 | // Node is the interface that all AST nodes must implement. | ||
8 | type 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 | ||
21 | type Pos struct { | ||
22 | Column, Line int // Column/Line number, starting at 1 | ||
23 | Filename string // Optional source filename, if known | ||
24 | } | ||
25 | |||
26 | func (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). | ||
36 | var 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. | ||
51 | type Visitor func(Node) Node | ||
52 | |||
53 | //go:generate stringer -type=Type | ||
54 | |||
55 | // Type is the type of any value. | ||
56 | type Type uint32 | ||
57 | |||
58 | const ( | ||
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 | |||
76 | func (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 @@ | |||
1 | package ast | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strings" | ||
6 | ) | ||
7 | |||
8 | // Call represents a function call. | ||
9 | type Call struct { | ||
10 | Func string | ||
11 | Args []Node | ||
12 | Posx Pos | ||
13 | } | ||
14 | |||
15 | func (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 | |||
23 | func (n *Call) Pos() Pos { | ||
24 | return n.Posx | ||
25 | } | ||
26 | |||
27 | func (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 | |||
36 | func (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 | |||
45 | func (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 @@ | |||
1 | package ast | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | ) | ||
6 | |||
7 | type 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. | ||
16 | func (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 | |||
24 | func (n *Conditional) Pos() Pos { | ||
25 | return n.Posx | ||
26 | } | ||
27 | |||
28 | func (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 | |||
34 | func (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 @@ | |||
1 | package ast | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strings" | ||
6 | ) | ||
7 | |||
8 | // Index represents an indexing operation into another data structure | ||
9 | type Index struct { | ||
10 | Target Node | ||
11 | Key Node | ||
12 | Posx Pos | ||
13 | } | ||
14 | |||
15 | func (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 | |||
21 | func (n *Index) Pos() Pos { | ||
22 | return n.Posx | ||
23 | } | ||
24 | |||
25 | func (n *Index) String() string { | ||
26 | return fmt.Sprintf("Index(%s, %s)", n.Target, n.Key) | ||
27 | } | ||
28 | |||
29 | func (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 | |||
50 | func (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 | |||
57 | func (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 | |||
64 | func 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 | |||
74 | func (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 @@ | |||
1 | package ast | ||
2 | |||
3 | import ( | ||
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. | ||
10 | type 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. | ||
26 | func 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. | ||
53 | func 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 | |||
61 | func (n *LiteralNode) Accept(v Visitor) Node { | ||
62 | return v(n) | ||
63 | } | ||
64 | |||
65 | func (n *LiteralNode) Pos() Pos { | ||
66 | return n.Posx | ||
67 | } | ||
68 | |||
69 | func (n *LiteralNode) GoString() string { | ||
70 | return fmt.Sprintf("*%#v", *n) | ||
71 | } | ||
72 | |||
73 | func (n *LiteralNode) String() string { | ||
74 | return fmt.Sprintf("Literal(%s, %v)", n.Typex, n.Value) | ||
75 | } | ||
76 | |||
77 | func (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. | ||
83 | func (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 @@ | |||
1 | package ast | ||
2 | |||
3 | import ( | ||
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. | ||
13 | type Output struct { | ||
14 | Exprs []Node | ||
15 | Posx Pos | ||
16 | } | ||
17 | |||
18 | func (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 | |||
26 | func (n *Output) Pos() Pos { | ||
27 | return n.Posx | ||
28 | } | ||
29 | |||
30 | func (n *Output) GoString() string { | ||
31 | return fmt.Sprintf("*%#v", *n) | ||
32 | } | ||
33 | |||
34 | func (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 | |||
43 | func (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 @@ | |||
1 | package ast | ||
2 | |||
3 | import ( | ||
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. | ||
10 | type 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. | ||
17 | type 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. | ||
24 | func 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. | ||
38 | func (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. | ||
45 | type 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. | ||
69 | type BasicScope struct { | ||
70 | FuncMap map[string]Function | ||
71 | VarMap map[string]Variable | ||
72 | } | ||
73 | |||
74 | func (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 | |||
83 | func (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 @@ | |||
1 | package ast | ||
2 | |||
3 | // Stack is a stack of Node. | ||
4 | type Stack struct { | ||
5 | stack []Node | ||
6 | } | ||
7 | |||
8 | func (s *Stack) Len() int { | ||
9 | return len(s.stack) | ||
10 | } | ||
11 | |||
12 | func (s *Stack) Push(n Node) { | ||
13 | s.stack = append(s.stack, n) | ||
14 | } | ||
15 | |||
16 | func (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 | |||
23 | func (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 | |||
3 | package ast | ||
4 | |||
5 | import "fmt" | ||
6 | |||
7 | const ( | ||
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 | |||
19 | var ( | ||
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 | |||
31 | func (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 @@ | |||
1 | package 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. | ||
5 | func 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 @@ | |||
1 | package ast | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | ) | ||
6 | |||
7 | // VariableAccess represents a variable access. | ||
8 | type VariableAccess struct { | ||
9 | Name string | ||
10 | Posx Pos | ||
11 | } | ||
12 | |||
13 | func (n *VariableAccess) Accept(v Visitor) Node { | ||
14 | return v(n) | ||
15 | } | ||
16 | |||
17 | func (n *VariableAccess) Pos() Pos { | ||
18 | return n.Posx | ||
19 | } | ||
20 | |||
21 | func (n *VariableAccess) GoString() string { | ||
22 | return fmt.Sprintf("*%#v", *n) | ||
23 | } | ||
24 | |||
25 | func (n *VariableAccess) String() string { | ||
26 | return fmt.Sprintf("Variable(%s)", n.Name) | ||
27 | } | ||
28 | |||
29 | func (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 @@ | |||
1 | package ast | ||
2 | |||
3 | import "fmt" | ||
4 | |||
5 | func 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 | |||
35 | func 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 @@ | |||
1 | package hil | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "strconv" | ||
6 | |||
7 | "github.com/hashicorp/hil/ast" | ||
8 | ) | ||
9 | |||
10 | // NOTE: All builtins are tested in engine_test.go | ||
11 | |||
12 | func 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 | |||
41 | func 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 | |||
69 | func 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 | |||
107 | func 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 | |||
129 | func 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 | |||
159 | func 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 | |||
189 | func 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 | |||
211 | func 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 | |||
237 | func 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 | |||
247 | func 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 | |||
258 | func 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 | |||
268 | func 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 | |||
278 | func 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 | |||
293 | func 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 | |||
308 | func 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 | |||
318 | func 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 @@ | |||
1 | package hil | ||
2 | |||
3 | import ( | ||
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. | ||
13 | type IdentifierCheck struct { | ||
14 | Scope ast.Scope | ||
15 | |||
16 | err error | ||
17 | lock sync.Mutex | ||
18 | } | ||
19 | |||
20 | func (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 | |||
28 | func (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 | |||
50 | func (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 | |||
73 | func (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 | |||
82 | func (c *IdentifierCheck) createErr(n ast.Node, str string) { | ||
83 | c.err = fmt.Errorf("%s: %s", n.Pos(), str) | ||
84 | } | ||
85 | |||
86 | func (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 @@ | |||
1 | package hil | ||
2 | |||
3 | import ( | ||
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. | ||
18 | type 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. | ||
38 | type TypeCheckNode interface { | ||
39 | TypeCheck(*TypeCheck) (ast.Node, error) | ||
40 | } | ||
41 | |||
42 | func (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 | |||
56 | func (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 | |||
104 | type typeCheckArithmetic struct { | ||
105 | n *ast.Arithmetic | ||
106 | } | ||
107 | |||
108 | func (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 | |||
136 | func (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 | |||
192 | func (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 | |||
285 | func (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 | |||
317 | type typeCheckCall struct { | ||
318 | n *ast.Call | ||
319 | } | ||
320 | |||
321 | func (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 | |||
390 | type typeCheckConditional struct { | ||
391 | n *ast.Conditional | ||
392 | } | ||
393 | |||
394 | func (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 | |||
474 | type typeCheckOutput struct { | ||
475 | n *ast.Output | ||
476 | } | ||
477 | |||
478 | func (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 | |||
530 | type typeCheckLiteral struct { | ||
531 | n *ast.LiteralNode | ||
532 | } | ||
533 | |||
534 | func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) { | ||
535 | v.StackPush(tc.n.Typex) | ||
536 | return tc.n, nil | ||
537 | } | ||
538 | |||
539 | type typeCheckVariableAccess struct { | ||
540 | n *ast.VariableAccess | ||
541 | } | ||
542 | |||
543 | func (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 | |||
557 | type typeCheckIndex struct { | ||
558 | n *ast.Index | ||
559 | } | ||
560 | |||
561 | func (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 | |||
624 | func (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 | |||
647 | func (v *TypeCheck) reset() { | ||
648 | v.Stack = nil | ||
649 | v.err = nil | ||
650 | } | ||
651 | |||
652 | func (v *TypeCheck) StackPush(t ast.Type) { | ||
653 | v.Stack = append(v.Stack, t) | ||
654 | } | ||
655 | |||
656 | func (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 | |||
662 | func (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 @@ | |||
1 | package hil | ||
2 | |||
3 | import ( | ||
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. | ||
14 | const UnknownValue = "74D93920-ED26-11E3-AC10-0800200C9A66" | ||
15 | |||
16 | var hilMapstructureDecodeHookSlice []interface{} | ||
17 | var hilMapstructureDecodeHookStringSlice []string | ||
18 | var 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. | ||
24 | func 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 | |||
49 | func 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 | |||
105 | func 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 @@ | |||
1 | package hil | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "errors" | ||
6 | "fmt" | ||
7 | "sync" | ||
8 | |||
9 | "github.com/hashicorp/hil/ast" | ||
10 | ) | ||
11 | |||
12 | // EvalConfig is the configuration for evaluating. | ||
13 | type 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. | ||
25 | type 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 | ||
37 | type 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. | ||
45 | var 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. | ||
49 | var errExitUnknown = errors.New("unknown value") | ||
50 | |||
51 | func 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. | ||
106 | func 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. | ||
161 | type EvalNode interface { | ||
162 | Eval(ast.Scope, *ast.Stack) (interface{}, ast.Type, error) | ||
163 | } | ||
164 | |||
165 | type evalVisitor struct { | ||
166 | Scope ast.Scope | ||
167 | Stack ast.Stack | ||
168 | |||
169 | err error | ||
170 | lock sync.Mutex | ||
171 | } | ||
172 | |||
173 | func (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 | |||
204 | func (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. | ||
237 | func 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 | |||
261 | type evalCall struct{ *ast.Call } | ||
262 | |||
263 | func (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 | |||
291 | type evalConditional struct{ *ast.Conditional } | ||
292 | |||
293 | func (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 | |||
313 | type evalIndex struct{ *ast.Index } | ||
314 | |||
315 | func (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 | |||
344 | func (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 | |||
374 | func (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 | |||
402 | type evalOutput struct{ *ast.Output } | ||
403 | |||
404 | func (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 | |||
455 | type evalLiteralNode struct{ *ast.LiteralNode } | ||
456 | |||
457 | func (v *evalLiteralNode) Eval(ast.Scope, *ast.Stack) (interface{}, ast.Type, error) { | ||
458 | return v.Value, v.Typex, nil | ||
459 | } | ||
460 | |||
461 | type evalVariableAccess struct{ *ast.VariableAccess } | ||
462 | |||
463 | func (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 @@ | |||
1 | package 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. | ||
7 | type EvalType uint32 | ||
8 | |||
9 | const ( | ||
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 | |||
3 | package hil | ||
4 | |||
5 | import "fmt" | ||
6 | |||
7 | const ( | ||
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 | |||
16 | var ( | ||
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 | |||
25 | func (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 @@ | |||
1 | package hil | ||
2 | |||
3 | import ( | ||
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. | ||
15 | func 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. | ||
26 | func 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 @@ | |||
1 | package parser | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/hil/ast" | ||
5 | "github.com/hashicorp/hil/scanner" | ||
6 | ) | ||
7 | |||
8 | var binaryOps []map[scanner.TokenType]ast.ArithmeticOp | ||
9 | |||
10 | func 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 @@ | |||
1 | package parser | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/hil/ast" | ||
7 | "github.com/hashicorp/hil/scanner" | ||
8 | ) | ||
9 | |||
10 | type ParseError struct { | ||
11 | Message string | ||
12 | Pos ast.Pos | ||
13 | } | ||
14 | |||
15 | func 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. | ||
24 | func TokenErrorf(token *scanner.Token, format string, args ...interface{}) error { | ||
25 | return Errorf(token.Pos, format, args...) | ||
26 | } | ||
27 | |||
28 | func ExpectationError(wanted string, got *scanner.Token) error { | ||
29 | return TokenErrorf(got, "expected %s but found %s", wanted, got) | ||
30 | } | ||
31 | |||
32 | func (e *ParseError) Error() string { | ||
33 | return fmt.Sprintf("parse error at %s: %s", e.Pos, e.Message) | ||
34 | } | ||
35 | |||
36 | func (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 | |||
3 | package parser | ||
4 | |||
5 | import ( | ||
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 | |||
18 | func 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 @@ | |||
1 | package parser | ||
2 | |||
3 | import ( | ||
4 | "strconv" | ||
5 | "unicode/utf8" | ||
6 | |||
7 | "github.com/hashicorp/hil/ast" | ||
8 | "github.com/hashicorp/hil/scanner" | ||
9 | ) | ||
10 | |||
11 | func 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 | |||
19 | type parser struct { | ||
20 | peeker *scanner.Peeker | ||
21 | } | ||
22 | |||
23 | func (p *parser) ParseTopLevel() (ast.Node, error) { | ||
24 | return p.parseInterpolationSeq(false) | ||
25 | } | ||
26 | |||
27 | func (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. | ||
35 | func (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. | ||
114 | func (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 | |||
175 | func (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 | |||
192 | func (p *parser) ParseExpression() (ast.Node, error) { | ||
193 | return p.parseTernaryCond() | ||
194 | } | ||
195 | |||
196 | func (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. | ||
248 | func (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 | |||
317 | func (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. | ||
439 | func (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. | ||
516 | func (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 @@ | |||
1 | package 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. | ||
7 | type Peeker struct { | ||
8 | ch <-chan *Token | ||
9 | peeked *Token | ||
10 | } | ||
11 | |||
12 | func 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. | ||
20 | func (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. | ||
28 | func (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. | ||
45 | func (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 @@ | |||
1 | package scanner | ||
2 | |||
3 | import ( | ||
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. | ||
23 | func Scan(s string, startPos ast.Pos) <-chan *Token { | ||
24 | ch := make(chan *Token) | ||
25 | go scan(s, ch, startPos) | ||
26 | return ch | ||
27 | } | ||
28 | |||
29 | func 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. | ||
43 | All: | ||
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. | ||
142 | func 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. | ||
325 | func 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. | ||
427 | func 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. | ||
471 | func 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. | ||
516 | func 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. | ||
528 | func 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 @@ | |||
1 | package scanner | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/hil/ast" | ||
7 | ) | ||
8 | |||
9 | type Token struct { | ||
10 | Type TokenType | ||
11 | Content string | ||
12 | Pos ast.Pos | ||
13 | } | ||
14 | |||
15 | //go:generate stringer -type=TokenType | ||
16 | type TokenType rune | ||
17 | |||
18 | const ( | ||
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 | |||
72 | func (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 | |||
3 | package scanner | ||
4 | |||
5 | import "fmt" | ||
6 | |||
7 | const _TokenType_name = "BANGBEGINPERCENTOPARENCPARENSTARPLUSCOMMAMINUSPERIODSLASHCOLONLTEQUALGTQUESTIONBOOLFLOATINTEGERSTRINGOBRACKETCBRACKETIDENTIFIERLITERALENDOQUOTECQUOTEANDORNOTEQUALLTEGTEEOFINVALID" | ||
8 | |||
9 | var _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 | |||
46 | func (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 @@ | |||
1 | package hil | ||
2 | |||
3 | import ( | ||
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. | ||
13 | func 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 @@ | |||
1 | package hil | ||
2 | |||
3 | import ( | ||
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. | ||
14 | type 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. | ||
21 | type 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. | ||
47 | func 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. | ||
55 | type 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 | |||
68 | func (w *interpolationWalker) Enter(loc reflectwalk.Location) error { | ||
69 | w.loc = loc | ||
70 | return nil | ||
71 | } | ||
72 | |||
73 | func (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 | |||
93 | func (w *interpolationWalker) Map(m reflect.Value) error { | ||
94 | w.cs = append(w.cs, m) | ||
95 | return nil | ||
96 | } | ||
97 | |||
98 | func (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 | |||
106 | func (w *interpolationWalker) Slice(s reflect.Value) error { | ||
107 | w.cs = append(w.cs, s) | ||
108 | return nil | ||
109 | } | ||
110 | |||
111 | func (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 | |||
117 | func (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 | |||
193 | func (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 | |||
214 | func (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 | |||
224 | func (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 | } | ||