diff options
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/function/stdlib')
6 files changed, 773 insertions, 30 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/datetime.go b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/datetime.go new file mode 100644 index 0000000..aa15b7b --- /dev/null +++ b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/datetime.go | |||
@@ -0,0 +1,385 @@ | |||
1 | package stdlib | ||
2 | |||
3 | import ( | ||
4 | "bufio" | ||
5 | "bytes" | ||
6 | "fmt" | ||
7 | "strings" | ||
8 | "time" | ||
9 | |||
10 | "github.com/zclconf/go-cty/cty" | ||
11 | "github.com/zclconf/go-cty/cty/function" | ||
12 | ) | ||
13 | |||
14 | var FormatDateFunc = function.New(&function.Spec{ | ||
15 | Params: []function.Parameter{ | ||
16 | { | ||
17 | Name: "format", | ||
18 | Type: cty.String, | ||
19 | }, | ||
20 | { | ||
21 | Name: "time", | ||
22 | Type: cty.String, | ||
23 | }, | ||
24 | }, | ||
25 | Type: function.StaticReturnType(cty.String), | ||
26 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | ||
27 | formatStr := args[0].AsString() | ||
28 | timeStr := args[1].AsString() | ||
29 | t, err := parseTimestamp(timeStr) | ||
30 | if err != nil { | ||
31 | return cty.DynamicVal, function.NewArgError(1, err) | ||
32 | } | ||
33 | |||
34 | var buf bytes.Buffer | ||
35 | sc := bufio.NewScanner(strings.NewReader(formatStr)) | ||
36 | sc.Split(splitDateFormat) | ||
37 | const esc = '\'' | ||
38 | for sc.Scan() { | ||
39 | tok := sc.Bytes() | ||
40 | |||
41 | // The leading byte signals the token type | ||
42 | switch { | ||
43 | case tok[0] == esc: | ||
44 | if tok[len(tok)-1] != esc || len(tok) == 1 { | ||
45 | return cty.DynamicVal, function.NewArgErrorf(0, "unterminated literal '") | ||
46 | } | ||
47 | if len(tok) == 2 { | ||
48 | // Must be a single escaped quote, '' | ||
49 | buf.WriteByte(esc) | ||
50 | } else { | ||
51 | // The content (until a closing esc) is printed out verbatim | ||
52 | // except that we must un-double any double-esc escapes in | ||
53 | // the middle of the string. | ||
54 | raw := tok[1 : len(tok)-1] | ||
55 | for i := 0; i < len(raw); i++ { | ||
56 | buf.WriteByte(raw[i]) | ||
57 | if raw[i] == esc { | ||
58 | i++ // skip the escaped quote | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | |||
63 | case startsDateFormatVerb(tok[0]): | ||
64 | switch tok[0] { | ||
65 | case 'Y': | ||
66 | y := t.Year() | ||
67 | switch len(tok) { | ||
68 | case 2: | ||
69 | fmt.Fprintf(&buf, "%02d", y%100) | ||
70 | case 4: | ||
71 | fmt.Fprintf(&buf, "%04d", y) | ||
72 | default: | ||
73 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: year must either be \"YY\" or \"YYYY\"", tok) | ||
74 | } | ||
75 | case 'M': | ||
76 | m := t.Month() | ||
77 | switch len(tok) { | ||
78 | case 1: | ||
79 | fmt.Fprintf(&buf, "%d", m) | ||
80 | case 2: | ||
81 | fmt.Fprintf(&buf, "%02d", m) | ||
82 | case 3: | ||
83 | buf.WriteString(m.String()[:3]) | ||
84 | case 4: | ||
85 | buf.WriteString(m.String()) | ||
86 | default: | ||
87 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: month must be \"M\", \"MM\", \"MMM\", or \"MMMM\"", tok) | ||
88 | } | ||
89 | case 'D': | ||
90 | d := t.Day() | ||
91 | switch len(tok) { | ||
92 | case 1: | ||
93 | fmt.Fprintf(&buf, "%d", d) | ||
94 | case 2: | ||
95 | fmt.Fprintf(&buf, "%02d", d) | ||
96 | default: | ||
97 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: day of month must either be \"D\" or \"DD\"", tok) | ||
98 | } | ||
99 | case 'E': | ||
100 | d := t.Weekday() | ||
101 | switch len(tok) { | ||
102 | case 3: | ||
103 | buf.WriteString(d.String()[:3]) | ||
104 | case 4: | ||
105 | buf.WriteString(d.String()) | ||
106 | default: | ||
107 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: day of week must either be \"EEE\" or \"EEEE\"", tok) | ||
108 | } | ||
109 | case 'h': | ||
110 | h := t.Hour() | ||
111 | switch len(tok) { | ||
112 | case 1: | ||
113 | fmt.Fprintf(&buf, "%d", h) | ||
114 | case 2: | ||
115 | fmt.Fprintf(&buf, "%02d", h) | ||
116 | default: | ||
117 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: 24-hour must either be \"h\" or \"hh\"", tok) | ||
118 | } | ||
119 | case 'H': | ||
120 | h := t.Hour() % 12 | ||
121 | if h == 0 { | ||
122 | h = 12 | ||
123 | } | ||
124 | switch len(tok) { | ||
125 | case 1: | ||
126 | fmt.Fprintf(&buf, "%d", h) | ||
127 | case 2: | ||
128 | fmt.Fprintf(&buf, "%02d", h) | ||
129 | default: | ||
130 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: 12-hour must either be \"H\" or \"HH\"", tok) | ||
131 | } | ||
132 | case 'A', 'a': | ||
133 | if len(tok) != 2 { | ||
134 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: must be \"%s%s\"", tok, tok[0:1], tok[0:1]) | ||
135 | } | ||
136 | upper := tok[0] == 'A' | ||
137 | switch t.Hour() / 12 { | ||
138 | case 0: | ||
139 | if upper { | ||
140 | buf.WriteString("AM") | ||
141 | } else { | ||
142 | buf.WriteString("am") | ||
143 | } | ||
144 | case 1: | ||
145 | if upper { | ||
146 | buf.WriteString("PM") | ||
147 | } else { | ||
148 | buf.WriteString("pm") | ||
149 | } | ||
150 | } | ||
151 | case 'm': | ||
152 | m := t.Minute() | ||
153 | switch len(tok) { | ||
154 | case 1: | ||
155 | fmt.Fprintf(&buf, "%d", m) | ||
156 | case 2: | ||
157 | fmt.Fprintf(&buf, "%02d", m) | ||
158 | default: | ||
159 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: minute must either be \"m\" or \"mm\"", tok) | ||
160 | } | ||
161 | case 's': | ||
162 | s := t.Second() | ||
163 | switch len(tok) { | ||
164 | case 1: | ||
165 | fmt.Fprintf(&buf, "%d", s) | ||
166 | case 2: | ||
167 | fmt.Fprintf(&buf, "%02d", s) | ||
168 | default: | ||
169 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: second must either be \"s\" or \"ss\"", tok) | ||
170 | } | ||
171 | case 'Z': | ||
172 | // We'll just lean on Go's own formatter for this one, since | ||
173 | // the necessary information is unexported. | ||
174 | switch len(tok) { | ||
175 | case 1: | ||
176 | buf.WriteString(t.Format("Z07:00")) | ||
177 | case 3: | ||
178 | str := t.Format("-0700") | ||
179 | switch str { | ||
180 | case "+0000": | ||
181 | buf.WriteString("UTC") | ||
182 | default: | ||
183 | buf.WriteString(str) | ||
184 | } | ||
185 | case 4: | ||
186 | buf.WriteString(t.Format("-0700")) | ||
187 | case 5: | ||
188 | buf.WriteString(t.Format("-07:00")) | ||
189 | default: | ||
190 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: timezone must be Z, ZZZZ, or ZZZZZ", tok) | ||
191 | } | ||
192 | default: | ||
193 | return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q", tok) | ||
194 | } | ||
195 | |||
196 | default: | ||
197 | // Any other starting character indicates a literal sequence | ||
198 | buf.Write(tok) | ||
199 | } | ||
200 | } | ||
201 | |||
202 | return cty.StringVal(buf.String()), nil | ||
203 | }, | ||
204 | }) | ||
205 | |||
206 | // FormatDate reformats a timestamp given in RFC3339 syntax into another time | ||
207 | // syntax defined by a given format string. | ||
208 | // | ||
209 | // The format string uses letter mnemonics to represent portions of the | ||
210 | // timestamp, with repetition signifying length variants of each portion. | ||
211 | // Single quote characters ' can be used to quote sequences of literal letters | ||
212 | // that should not be interpreted as formatting mnemonics. | ||
213 | // | ||
214 | // The full set of supported mnemonic sequences is listed below: | ||
215 | // | ||
216 | // YY Year modulo 100 zero-padded to two digits, like "06". | ||
217 | // YYYY Four (or more) digit year, like "2006". | ||
218 | // M Month number, like "1" for January. | ||
219 | // MM Month number zero-padded to two digits, like "01". | ||
220 | // MMM English month name abbreviated to three letters, like "Jan". | ||
221 | // MMMM English month name unabbreviated, like "January". | ||
222 | // D Day of month number, like "2". | ||
223 | // DD Day of month number zero-padded to two digits, like "02". | ||
224 | // EEE English day of week name abbreviated to three letters, like "Mon". | ||
225 | // EEEE English day of week name unabbreviated, like "Monday". | ||
226 | // h 24-hour number, like "2". | ||
227 | // hh 24-hour number zero-padded to two digits, like "02". | ||
228 | // H 12-hour number, like "2". | ||
229 | // HH 12-hour number zero-padded to two digits, like "02". | ||
230 | // AA Hour AM/PM marker in uppercase, like "AM". | ||
231 | // aa Hour AM/PM marker in lowercase, like "am". | ||
232 | // m Minute within hour, like "5". | ||
233 | // mm Minute within hour zero-padded to two digits, like "05". | ||
234 | // s Second within minute, like "9". | ||
235 | // ss Second within minute zero-padded to two digits, like "09". | ||
236 | // ZZZZ Timezone offset with just sign and digit, like "-0800". | ||
237 | // ZZZZZ Timezone offset with colon separating hours and minutes, like "-08:00". | ||
238 | // Z Like ZZZZZ but with a special case "Z" for UTC. | ||
239 | // ZZZ Like ZZZZ but with a special case "UTC" for UTC. | ||
240 | // | ||
241 | // The format syntax is optimized mainly for generating machine-oriented | ||
242 | // timestamps rather than human-oriented timestamps; the English language | ||
243 | // portions of the output reflect the use of English names in a number of | ||
244 | // machine-readable date formatting standards. For presentation to humans, | ||
245 | // a locale-aware time formatter (not included in this package) is a better | ||
246 | // choice. | ||
247 | // | ||
248 | // The format syntax is not compatible with that of any other language, but | ||
249 | // is optimized so that patterns for common standard date formats can be | ||
250 | // recognized quickly even by a reader unfamiliar with the format syntax. | ||
251 | func FormatDate(format cty.Value, timestamp cty.Value) (cty.Value, error) { | ||
252 | return FormatDateFunc.Call([]cty.Value{format, timestamp}) | ||
253 | } | ||
254 | |||
255 | func parseTimestamp(ts string) (time.Time, error) { | ||
256 | t, err := time.Parse(time.RFC3339, ts) | ||
257 | if err != nil { | ||
258 | switch err := err.(type) { | ||
259 | case *time.ParseError: | ||
260 | // If err is s time.ParseError then its string representation is not | ||
261 | // appropriate since it relies on details of Go's strange date format | ||
262 | // representation, which a caller of our functions is not expected | ||
263 | // to be familiar with. | ||
264 | // | ||
265 | // Therefore we do some light transformation to get a more suitable | ||
266 | // error that should make more sense to our callers. These are | ||
267 | // still not awesome error messages, but at least they refer to | ||
268 | // the timestamp portions by name rather than by Go's example | ||
269 | // values. | ||
270 | if err.LayoutElem == "" && err.ValueElem == "" && err.Message != "" { | ||
271 | // For some reason err.Message is populated with a ": " prefix | ||
272 | // by the time package. | ||
273 | return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp%s", err.Message) | ||
274 | } | ||
275 | var what string | ||
276 | switch err.LayoutElem { | ||
277 | case "2006": | ||
278 | what = "year" | ||
279 | case "01": | ||
280 | what = "month" | ||
281 | case "02": | ||
282 | what = "day of month" | ||
283 | case "15": | ||
284 | what = "hour" | ||
285 | case "04": | ||
286 | what = "minute" | ||
287 | case "05": | ||
288 | what = "second" | ||
289 | case "Z07:00": | ||
290 | what = "UTC offset" | ||
291 | case "T": | ||
292 | return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: missing required time introducer 'T'") | ||
293 | case ":", "-": | ||
294 | if err.ValueElem == "" { | ||
295 | return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string where %q is expected", err.LayoutElem) | ||
296 | } else { | ||
297 | return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: found %q where %q is expected", err.ValueElem, err.LayoutElem) | ||
298 | } | ||
299 | default: | ||
300 | // Should never get here, because time.RFC3339 includes only the | ||
301 | // above portions, but since that might change in future we'll | ||
302 | // be robust here. | ||
303 | what = "timestamp segment" | ||
304 | } | ||
305 | if err.ValueElem == "" { | ||
306 | return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string before %s", what) | ||
307 | } else { | ||
308 | return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: cannot use %q as %s", err.ValueElem, what) | ||
309 | } | ||
310 | } | ||
311 | return time.Time{}, err | ||
312 | } | ||
313 | return t, nil | ||
314 | } | ||
315 | |||
316 | // splitDataFormat is a bufio.SplitFunc used to tokenize a date format. | ||
317 | func splitDateFormat(data []byte, atEOF bool) (advance int, token []byte, err error) { | ||
318 | if len(data) == 0 { | ||
319 | return 0, nil, nil | ||
320 | } | ||
321 | |||
322 | const esc = '\'' | ||
323 | |||
324 | switch { | ||
325 | |||
326 | case data[0] == esc: | ||
327 | // If we have another quote immediately after then this is a single | ||
328 | // escaped escape. | ||
329 | if len(data) > 1 && data[1] == esc { | ||
330 | return 2, data[:2], nil | ||
331 | } | ||
332 | |||
333 | // Beginning of quoted sequence, so we will seek forward until we find | ||
334 | // the closing quote, ignoring escaped quotes along the way. | ||
335 | for i := 1; i < len(data); i++ { | ||
336 | if data[i] == esc { | ||
337 | if (i + 1) == len(data) { | ||
338 | // We need at least one more byte to decide if this is an | ||
339 | // escape or a terminator. | ||
340 | return 0, nil, nil | ||
341 | } | ||
342 | if data[i+1] == esc { | ||
343 | i++ // doubled-up quotes are an escape sequence | ||
344 | continue | ||
345 | } | ||
346 | // We've found the closing quote | ||
347 | return i + 1, data[:i+1], nil | ||
348 | } | ||
349 | } | ||
350 | // If we fall out here then we need more bytes to find the end, | ||
351 | // unless we're already at the end with an unclosed quote. | ||
352 | if atEOF { | ||
353 | return len(data), data, nil | ||
354 | } | ||
355 | return 0, nil, nil | ||
356 | |||
357 | case startsDateFormatVerb(data[0]): | ||
358 | rep := data[0] | ||
359 | for i := 1; i < len(data); i++ { | ||
360 | if data[i] != rep { | ||
361 | return i, data[:i], nil | ||
362 | } | ||
363 | } | ||
364 | if atEOF { | ||
365 | return len(data), data, nil | ||
366 | } | ||
367 | // We need more data to decide if we've found the end | ||
368 | return 0, nil, nil | ||
369 | |||
370 | default: | ||
371 | for i := 1; i < len(data); i++ { | ||
372 | if data[i] == esc || startsDateFormatVerb(data[i]) { | ||
373 | return i, data[:i], nil | ||
374 | } | ||
375 | } | ||
376 | // We might not actually be at the end of a literal sequence, | ||
377 | // but that doesn't matter since we'll concat them back together | ||
378 | // anyway. | ||
379 | return len(data), data, nil | ||
380 | } | ||
381 | } | ||
382 | |||
383 | func startsDateFormatVerb(b byte) bool { | ||
384 | return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') | ||
385 | } | ||
diff --git a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go index fb24f20..664790b 100644 --- a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go +++ b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go | |||
@@ -84,6 +84,11 @@ var FormatListFunc = function.New(&function.Spec{ | |||
84 | argTy := arg.Type() | 84 | argTy := arg.Type() |
85 | switch { | 85 | switch { |
86 | case (argTy.IsListType() || argTy.IsSetType() || argTy.IsTupleType()) && !arg.IsNull(): | 86 | case (argTy.IsListType() || argTy.IsSetType() || argTy.IsTupleType()) && !arg.IsNull(): |
87 | if !argTy.IsTupleType() && !arg.IsKnown() { | ||
88 | // We can't iterate this one at all yet then, so we can't | ||
89 | // yet produce a result. | ||
90 | return cty.UnknownVal(retType), nil | ||
91 | } | ||
87 | thisLen := arg.LengthInt() | 92 | thisLen := arg.LengthInt() |
88 | if iterLen == -1 { | 93 | if iterLen == -1 { |
89 | iterLen = thisLen | 94 | iterLen = thisLen |
diff --git a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.go b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.go index 86876ba..32b1ac9 100644 --- a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.go +++ b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.go | |||
@@ -11,9 +11,10 @@ import ( | |||
11 | "unicode/utf8" | 11 | "unicode/utf8" |
12 | 12 | ||
13 | "github.com/zclconf/go-cty/cty" | 13 | "github.com/zclconf/go-cty/cty" |
14 | "github.com/zclconf/go-cty/cty/function" | ||
14 | ) | 15 | ) |
15 | 16 | ||
16 | // line 20 "format_fsm.go" | 17 | // line 21 "format_fsm.go" |
17 | var _formatfsm_actions []byte = []byte{ | 18 | var _formatfsm_actions []byte = []byte{ |
18 | 0, 1, 0, 1, 1, 1, 2, 1, 4, | 19 | 0, 1, 0, 1, 1, 1, 2, 1, 4, |
19 | 1, 5, 1, 6, 1, 7, 1, 8, | 20 | 1, 5, 1, 6, 1, 7, 1, 8, |
@@ -86,15 +87,16 @@ const formatfsm_error int = 0 | |||
86 | 87 | ||
87 | const formatfsm_en_main int = 8 | 88 | const formatfsm_en_main int = 8 |
88 | 89 | ||
89 | // line 19 "format_fsm.rl" | 90 | // line 20 "format_fsm.rl" |
90 | 91 | ||
91 | func formatFSM(format string, a []cty.Value) (string, error) { | 92 | func formatFSM(format string, a []cty.Value) (string, error) { |
92 | var buf bytes.Buffer | 93 | var buf bytes.Buffer |
93 | data := format | 94 | data := format |
94 | nextArg := 1 // arg numbers are 1-based | 95 | nextArg := 1 // arg numbers are 1-based |
95 | var verb formatVerb | 96 | var verb formatVerb |
97 | highestArgIdx := 0 // zero means "none", since arg numbers are 1-based | ||
96 | 98 | ||
97 | // line 153 "format_fsm.rl" | 99 | // line 159 "format_fsm.rl" |
98 | 100 | ||
99 | // Ragel state | 101 | // Ragel state |
100 | p := 0 // "Pointer" into data | 102 | p := 0 // "Pointer" into data |
@@ -109,12 +111,12 @@ func formatFSM(format string, a []cty.Value) (string, error) { | |||
109 | _ = te | 111 | _ = te |
110 | _ = eof | 112 | _ = eof |
111 | 113 | ||
112 | // line 121 "format_fsm.go" | 114 | // line 123 "format_fsm.go" |
113 | { | 115 | { |
114 | cs = formatfsm_start | 116 | cs = formatfsm_start |
115 | } | 117 | } |
116 | 118 | ||
117 | // line 126 "format_fsm.go" | 119 | // line 128 "format_fsm.go" |
118 | { | 120 | { |
119 | var _klen int | 121 | var _klen int |
120 | var _trans int | 122 | var _trans int |
@@ -195,7 +197,7 @@ func formatFSM(format string, a []cty.Value) (string, error) { | |||
195 | _acts++ | 197 | _acts++ |
196 | switch _formatfsm_actions[_acts-1] { | 198 | switch _formatfsm_actions[_acts-1] { |
197 | case 0: | 199 | case 0: |
198 | // line 29 "format_fsm.rl" | 200 | // line 31 "format_fsm.rl" |
199 | 201 | ||
200 | verb = formatVerb{ | 202 | verb = formatVerb{ |
201 | ArgNum: nextArg, | 203 | ArgNum: nextArg, |
@@ -205,12 +207,12 @@ func formatFSM(format string, a []cty.Value) (string, error) { | |||
205 | ts = p | 207 | ts = p |
206 | 208 | ||
207 | case 1: | 209 | case 1: |
208 | // line 38 "format_fsm.rl" | 210 | // line 40 "format_fsm.rl" |
209 | 211 | ||
210 | buf.WriteByte(data[p]) | 212 | buf.WriteByte(data[p]) |
211 | 213 | ||
212 | case 4: | 214 | case 4: |
213 | // line 49 "format_fsm.rl" | 215 | // line 51 "format_fsm.rl" |
214 | 216 | ||
215 | // We'll try to slurp a whole UTF-8 sequence here, to give the user | 217 | // We'll try to slurp a whole UTF-8 sequence here, to give the user |
216 | // better feedback. | 218 | // better feedback. |
@@ -218,85 +220,89 @@ func formatFSM(format string, a []cty.Value) (string, error) { | |||
218 | return buf.String(), fmt.Errorf("unrecognized format character %q at offset %d", r, p) | 220 | return buf.String(), fmt.Errorf("unrecognized format character %q at offset %d", r, p) |
219 | 221 | ||
220 | case 5: | 222 | case 5: |
221 | // line 56 "format_fsm.rl" | 223 | // line 58 "format_fsm.rl" |
222 | 224 | ||
223 | verb.Sharp = true | 225 | verb.Sharp = true |
224 | 226 | ||
225 | case 6: | 227 | case 6: |
226 | // line 59 "format_fsm.rl" | 228 | // line 61 "format_fsm.rl" |
227 | 229 | ||
228 | verb.Zero = true | 230 | verb.Zero = true |
229 | 231 | ||
230 | case 7: | 232 | case 7: |
231 | // line 62 "format_fsm.rl" | 233 | // line 64 "format_fsm.rl" |
232 | 234 | ||
233 | verb.Minus = true | 235 | verb.Minus = true |
234 | 236 | ||
235 | case 8: | 237 | case 8: |
236 | // line 65 "format_fsm.rl" | 238 | // line 67 "format_fsm.rl" |
237 | 239 | ||
238 | verb.Plus = true | 240 | verb.Plus = true |
239 | 241 | ||
240 | case 9: | 242 | case 9: |
241 | // line 68 "format_fsm.rl" | 243 | // line 70 "format_fsm.rl" |
242 | 244 | ||
243 | verb.Space = true | 245 | verb.Space = true |
244 | 246 | ||
245 | case 10: | 247 | case 10: |
246 | // line 72 "format_fsm.rl" | 248 | // line 74 "format_fsm.rl" |
247 | 249 | ||
248 | verb.ArgNum = 0 | 250 | verb.ArgNum = 0 |
249 | 251 | ||
250 | case 11: | 252 | case 11: |
251 | // line 75 "format_fsm.rl" | 253 | // line 77 "format_fsm.rl" |
252 | 254 | ||
253 | verb.ArgNum = (10 * verb.ArgNum) + (int(data[p]) - '0') | 255 | verb.ArgNum = (10 * verb.ArgNum) + (int(data[p]) - '0') |
254 | 256 | ||
255 | case 12: | 257 | case 12: |
256 | // line 79 "format_fsm.rl" | 258 | // line 81 "format_fsm.rl" |
257 | 259 | ||
258 | verb.HasWidth = true | 260 | verb.HasWidth = true |
259 | 261 | ||
260 | case 13: | 262 | case 13: |
261 | // line 82 "format_fsm.rl" | 263 | // line 84 "format_fsm.rl" |
262 | 264 | ||
263 | verb.Width = 0 | 265 | verb.Width = 0 |
264 | 266 | ||
265 | case 14: | 267 | case 14: |
266 | // line 85 "format_fsm.rl" | 268 | // line 87 "format_fsm.rl" |
267 | 269 | ||
268 | verb.Width = (10 * verb.Width) + (int(data[p]) - '0') | 270 | verb.Width = (10 * verb.Width) + (int(data[p]) - '0') |
269 | 271 | ||
270 | case 15: | 272 | case 15: |
271 | // line 89 "format_fsm.rl" | 273 | // line 91 "format_fsm.rl" |
272 | 274 | ||
273 | verb.HasPrec = true | 275 | verb.HasPrec = true |
274 | 276 | ||
275 | case 16: | 277 | case 16: |
276 | // line 92 "format_fsm.rl" | 278 | // line 94 "format_fsm.rl" |
277 | 279 | ||
278 | verb.Prec = 0 | 280 | verb.Prec = 0 |
279 | 281 | ||
280 | case 17: | 282 | case 17: |
281 | // line 95 "format_fsm.rl" | 283 | // line 97 "format_fsm.rl" |
282 | 284 | ||
283 | verb.Prec = (10 * verb.Prec) + (int(data[p]) - '0') | 285 | verb.Prec = (10 * verb.Prec) + (int(data[p]) - '0') |
284 | 286 | ||
285 | case 18: | 287 | case 18: |
286 | // line 99 "format_fsm.rl" | 288 | // line 101 "format_fsm.rl" |
287 | 289 | ||
288 | verb.Mode = rune(data[p]) | 290 | verb.Mode = rune(data[p]) |
289 | te = p + 1 | 291 | te = p + 1 |
290 | verb.Raw = data[ts:te] | 292 | verb.Raw = data[ts:te] |
291 | verb.Offset = ts | 293 | verb.Offset = ts |
292 | 294 | ||
295 | if verb.ArgNum > highestArgIdx { | ||
296 | highestArgIdx = verb.ArgNum | ||
297 | } | ||
298 | |||
293 | err := formatAppend(&verb, &buf, a) | 299 | err := formatAppend(&verb, &buf, a) |
294 | if err != nil { | 300 | if err != nil { |
295 | return buf.String(), err | 301 | return buf.String(), err |
296 | } | 302 | } |
297 | nextArg = verb.ArgNum + 1 | 303 | nextArg = verb.ArgNum + 1 |
298 | 304 | ||
299 | // line 324 "format_fsm.go" | 305 | // line 330 "format_fsm.go" |
300 | } | 306 | } |
301 | } | 307 | } |
302 | 308 | ||
@@ -319,22 +325,22 @@ func formatFSM(format string, a []cty.Value) (string, error) { | |||
319 | __acts++ | 325 | __acts++ |
320 | switch _formatfsm_actions[__acts-1] { | 326 | switch _formatfsm_actions[__acts-1] { |
321 | case 2: | 327 | case 2: |
322 | // line 42 "format_fsm.rl" | 328 | // line 44 "format_fsm.rl" |
323 | 329 | ||
324 | case 3: | 330 | case 3: |
325 | // line 45 "format_fsm.rl" | 331 | // line 47 "format_fsm.rl" |
326 | 332 | ||
327 | return buf.String(), fmt.Errorf("invalid format string starting at offset %d", p) | 333 | return buf.String(), fmt.Errorf("invalid format string starting at offset %d", p) |
328 | 334 | ||
329 | case 4: | 335 | case 4: |
330 | // line 49 "format_fsm.rl" | 336 | // line 51 "format_fsm.rl" |
331 | 337 | ||
332 | // We'll try to slurp a whole UTF-8 sequence here, to give the user | 338 | // We'll try to slurp a whole UTF-8 sequence here, to give the user |
333 | // better feedback. | 339 | // better feedback. |
334 | r, _ := utf8.DecodeRuneInString(data[p:]) | 340 | r, _ := utf8.DecodeRuneInString(data[p:]) |
335 | return buf.String(), fmt.Errorf("unrecognized format character %q at offset %d", r, p) | 341 | return buf.String(), fmt.Errorf("unrecognized format character %q at offset %d", r, p) |
336 | 342 | ||
337 | // line 363 "format_fsm.go" | 343 | // line 369 "format_fsm.go" |
338 | } | 344 | } |
339 | } | 345 | } |
340 | } | 346 | } |
@@ -344,14 +350,24 @@ func formatFSM(format string, a []cty.Value) (string, error) { | |||
344 | } | 350 | } |
345 | } | 351 | } |
346 | 352 | ||
347 | // line 171 "format_fsm.rl" | 353 | // line 177 "format_fsm.rl" |
348 | 354 | ||
349 | // If we fall out here without being in a final state then we've | 355 | // If we fall out here without being in a final state then we've |
350 | // encountered something that the scanner can't match, which should | 356 | // encountered something that the scanner can't match, which should |
351 | // be impossible (the scanner matches all bytes _somehow_) but we'll | 357 | // be impossible (the scanner matches all bytes _somehow_) but we'll |
352 | // flag it anyway rather than just losing data from the end. | 358 | // flag it anyway rather than just losing data from the end. |
353 | if cs < formatfsm_first_final { | 359 | if cs < formatfsm_first_final { |
354 | return buf.String(), fmt.Errorf("extraneous characters beginning at offset %i", p) | 360 | return buf.String(), fmt.Errorf("extraneous characters beginning at offset %d", p) |
361 | } | ||
362 | |||
363 | if highestArgIdx < len(a) { | ||
364 | // Extraneous args are an error, to more easily detect mistakes | ||
365 | firstBad := highestArgIdx + 1 | ||
366 | if highestArgIdx == 0 { | ||
367 | // Custom error message for this case | ||
368 | return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; no verbs in format string") | ||
369 | } | ||
370 | return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; only %d used by format string", highestArgIdx) | ||
355 | } | 371 | } |
356 | 372 | ||
357 | return buf.String(), nil | 373 | return buf.String(), nil |
diff --git a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.rl b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.rl index 85d43bb..3c642d9 100644 --- a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.rl +++ b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format_fsm.rl | |||
@@ -12,6 +12,7 @@ import ( | |||
12 | "unicode/utf8" | 12 | "unicode/utf8" |
13 | 13 | ||
14 | "github.com/zclconf/go-cty/cty" | 14 | "github.com/zclconf/go-cty/cty" |
15 | "github.com/zclconf/go-cty/cty/function" | ||
15 | ) | 16 | ) |
16 | 17 | ||
17 | %%{ | 18 | %%{ |
@@ -23,6 +24,7 @@ func formatFSM(format string, a []cty.Value) (string, error) { | |||
23 | data := format | 24 | data := format |
24 | nextArg := 1 // arg numbers are 1-based | 25 | nextArg := 1 // arg numbers are 1-based |
25 | var verb formatVerb | 26 | var verb formatVerb |
27 | highestArgIdx := 0 // zero means "none", since arg numbers are 1-based | ||
26 | 28 | ||
27 | %%{ | 29 | %%{ |
28 | 30 | ||
@@ -102,6 +104,10 @@ func formatFSM(format string, a []cty.Value) (string, error) { | |||
102 | verb.Raw = data[ts:te] | 104 | verb.Raw = data[ts:te] |
103 | verb.Offset = ts | 105 | verb.Offset = ts |
104 | 106 | ||
107 | if verb.ArgNum > highestArgIdx { | ||
108 | highestArgIdx = verb.ArgNum | ||
109 | } | ||
110 | |||
105 | err := formatAppend(&verb, &buf, a) | 111 | err := formatAppend(&verb, &buf, a) |
106 | if err != nil { | 112 | if err != nil { |
107 | return buf.String(), err | 113 | return buf.String(), err |
@@ -175,7 +181,17 @@ func formatFSM(format string, a []cty.Value) (string, error) { | |||
175 | // be impossible (the scanner matches all bytes _somehow_) but we'll | 181 | // be impossible (the scanner matches all bytes _somehow_) but we'll |
176 | // flag it anyway rather than just losing data from the end. | 182 | // flag it anyway rather than just losing data from the end. |
177 | if cs < formatfsm_first_final { | 183 | if cs < formatfsm_first_final { |
178 | return buf.String(), fmt.Errorf("extraneous characters beginning at offset %i", p) | 184 | return buf.String(), fmt.Errorf("extraneous characters beginning at offset %d", p) |
185 | } | ||
186 | |||
187 | if highestArgIdx < len(a) { | ||
188 | // Extraneous args are an error, to more easily detect mistakes | ||
189 | firstBad := highestArgIdx+1 | ||
190 | if highestArgIdx == 0 { | ||
191 | // Custom error message for this case | ||
192 | return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; no verbs in format string") | ||
193 | } | ||
194 | return buf.String(), function.NewArgErrorf(firstBad, "too many arguments; only %d used by format string", highestArgIdx) | ||
179 | } | 195 | } |
180 | 196 | ||
181 | return buf.String(), nil | 197 | return buf.String(), nil |
diff --git a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/regexp.go b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/regexp.go new file mode 100644 index 0000000..2dd6348 --- /dev/null +++ b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/regexp.go | |||
@@ -0,0 +1,233 @@ | |||
1 | package stdlib | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "regexp" | ||
6 | resyntax "regexp/syntax" | ||
7 | |||
8 | "github.com/zclconf/go-cty/cty" | ||
9 | "github.com/zclconf/go-cty/cty/function" | ||
10 | ) | ||
11 | |||
12 | var RegexFunc = function.New(&function.Spec{ | ||
13 | Params: []function.Parameter{ | ||
14 | { | ||
15 | Name: "pattern", | ||
16 | Type: cty.String, | ||
17 | }, | ||
18 | { | ||
19 | Name: "string", | ||
20 | Type: cty.String, | ||
21 | }, | ||
22 | }, | ||
23 | Type: func(args []cty.Value) (cty.Type, error) { | ||
24 | if !args[0].IsKnown() { | ||
25 | // We can't predict our type without seeing our pattern | ||
26 | return cty.DynamicPseudoType, nil | ||
27 | } | ||
28 | |||
29 | retTy, err := regexPatternResultType(args[0].AsString()) | ||
30 | if err != nil { | ||
31 | err = function.NewArgError(0, err) | ||
32 | } | ||
33 | return retTy, err | ||
34 | }, | ||
35 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | ||
36 | if retType == cty.DynamicPseudoType { | ||
37 | return cty.DynamicVal, nil | ||
38 | } | ||
39 | |||
40 | re, err := regexp.Compile(args[0].AsString()) | ||
41 | if err != nil { | ||
42 | // Should never happen, since we checked this in the Type function above. | ||
43 | return cty.NilVal, function.NewArgErrorf(0, "error parsing pattern: %s", err) | ||
44 | } | ||
45 | str := args[1].AsString() | ||
46 | |||
47 | captureIdxs := re.FindStringSubmatchIndex(str) | ||
48 | if captureIdxs == nil { | ||
49 | return cty.NilVal, fmt.Errorf("pattern did not match any part of the given string") | ||
50 | } | ||
51 | |||
52 | return regexPatternResult(re, str, captureIdxs, retType), nil | ||
53 | }, | ||
54 | }) | ||
55 | |||
56 | var RegexAllFunc = function.New(&function.Spec{ | ||
57 | Params: []function.Parameter{ | ||
58 | { | ||
59 | Name: "pattern", | ||
60 | Type: cty.String, | ||
61 | }, | ||
62 | { | ||
63 | Name: "string", | ||
64 | Type: cty.String, | ||
65 | }, | ||
66 | }, | ||
67 | Type: func(args []cty.Value) (cty.Type, error) { | ||
68 | if !args[0].IsKnown() { | ||
69 | // We can't predict our type without seeing our pattern, | ||
70 | // but we do know it'll always be a list of something. | ||
71 | return cty.List(cty.DynamicPseudoType), nil | ||
72 | } | ||
73 | |||
74 | retTy, err := regexPatternResultType(args[0].AsString()) | ||
75 | if err != nil { | ||
76 | err = function.NewArgError(0, err) | ||
77 | } | ||
78 | return cty.List(retTy), err | ||
79 | }, | ||
80 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | ||
81 | ety := retType.ElementType() | ||
82 | if ety == cty.DynamicPseudoType { | ||
83 | return cty.DynamicVal, nil | ||
84 | } | ||
85 | |||
86 | re, err := regexp.Compile(args[0].AsString()) | ||
87 | if err != nil { | ||
88 | // Should never happen, since we checked this in the Type function above. | ||
89 | return cty.NilVal, function.NewArgErrorf(0, "error parsing pattern: %s", err) | ||
90 | } | ||
91 | str := args[1].AsString() | ||
92 | |||
93 | captureIdxsEach := re.FindAllStringSubmatchIndex(str, -1) | ||
94 | if len(captureIdxsEach) == 0 { | ||
95 | return cty.ListValEmpty(ety), nil | ||
96 | } | ||
97 | |||
98 | elems := make([]cty.Value, len(captureIdxsEach)) | ||
99 | for i, captureIdxs := range captureIdxsEach { | ||
100 | elems[i] = regexPatternResult(re, str, captureIdxs, ety) | ||
101 | } | ||
102 | return cty.ListVal(elems), nil | ||
103 | }, | ||
104 | }) | ||
105 | |||
106 | // Regex is a function that extracts one or more substrings from a given | ||
107 | // string by applying a regular expression pattern, describing the first | ||
108 | // match. | ||
109 | // | ||
110 | // The return type depends on the composition of the capture groups (if any) | ||
111 | // in the pattern: | ||
112 | // | ||
113 | // - If there are no capture groups at all, the result is a single string | ||
114 | // representing the entire matched pattern. | ||
115 | // - If all of the capture groups are named, the result is an object whose | ||
116 | // keys are the named groups and whose values are their sub-matches, or | ||
117 | // null if a particular sub-group was inside another group that didn't | ||
118 | // match. | ||
119 | // - If none of the capture groups are named, the result is a tuple whose | ||
120 | // elements are the sub-groups in order and whose values are their | ||
121 | // sub-matches, or null if a particular sub-group was inside another group | ||
122 | // that didn't match. | ||
123 | // - It is invalid to use both named and un-named capture groups together in | ||
124 | // the same pattern. | ||
125 | // | ||
126 | // If the pattern doesn't match, this function returns an error. To test for | ||
127 | // a match, call RegexAll and check if the length of the result is greater | ||
128 | // than zero. | ||
129 | func Regex(pattern, str cty.Value) (cty.Value, error) { | ||
130 | return RegexFunc.Call([]cty.Value{pattern, str}) | ||
131 | } | ||
132 | |||
133 | // RegexAll is similar to Regex but it finds all of the non-overlapping matches | ||
134 | // in the given string and returns a list of them. | ||
135 | // | ||
136 | // The result type is always a list, whose element type is deduced from the | ||
137 | // pattern in the same way as the return type for Regex is decided. | ||
138 | // | ||
139 | // If the pattern doesn't match at all, this function returns an empty list. | ||
140 | func RegexAll(pattern, str cty.Value) (cty.Value, error) { | ||
141 | return RegexAllFunc.Call([]cty.Value{pattern, str}) | ||
142 | } | ||
143 | |||
144 | // regexPatternResultType parses the given regular expression pattern and | ||
145 | // returns the structural type that would be returned to represent its | ||
146 | // capture groups. | ||
147 | // | ||
148 | // Returns an error if parsing fails or if the pattern uses a mixture of | ||
149 | // named and unnamed capture groups, which is not permitted. | ||
150 | func regexPatternResultType(pattern string) (cty.Type, error) { | ||
151 | re, rawErr := regexp.Compile(pattern) | ||
152 | switch err := rawErr.(type) { | ||
153 | case *resyntax.Error: | ||
154 | return cty.NilType, fmt.Errorf("invalid regexp pattern: %s in %s", err.Code, err.Expr) | ||
155 | case error: | ||
156 | // Should never happen, since all regexp compile errors should | ||
157 | // be resyntax.Error, but just in case... | ||
158 | return cty.NilType, fmt.Errorf("error parsing pattern: %s", err) | ||
159 | } | ||
160 | |||
161 | allNames := re.SubexpNames()[1:] | ||
162 | var names []string | ||
163 | unnamed := 0 | ||
164 | for _, name := range allNames { | ||
165 | if name == "" { | ||
166 | unnamed++ | ||
167 | } else { | ||
168 | if names == nil { | ||
169 | names = make([]string, 0, len(allNames)) | ||
170 | } | ||
171 | names = append(names, name) | ||
172 | } | ||
173 | } | ||
174 | switch { | ||
175 | case unnamed == 0 && len(names) == 0: | ||
176 | // If there are no capture groups at all then we'll return just a | ||
177 | // single string for the whole match. | ||
178 | return cty.String, nil | ||
179 | case unnamed > 0 && len(names) > 0: | ||
180 | return cty.NilType, fmt.Errorf("invalid regexp pattern: cannot mix both named and unnamed capture groups") | ||
181 | case unnamed > 0: | ||
182 | // For unnamed captures, we return a tuple of them all in order. | ||
183 | etys := make([]cty.Type, unnamed) | ||
184 | for i := range etys { | ||
185 | etys[i] = cty.String | ||
186 | } | ||
187 | return cty.Tuple(etys), nil | ||
188 | default: | ||
189 | // For named captures, we return an object using the capture names | ||
190 | // as keys. | ||
191 | atys := make(map[string]cty.Type, len(names)) | ||
192 | for _, name := range names { | ||
193 | atys[name] = cty.String | ||
194 | } | ||
195 | return cty.Object(atys), nil | ||
196 | } | ||
197 | } | ||
198 | |||
199 | func regexPatternResult(re *regexp.Regexp, str string, captureIdxs []int, retType cty.Type) cty.Value { | ||
200 | switch { | ||
201 | case retType == cty.String: | ||
202 | start, end := captureIdxs[0], captureIdxs[1] | ||
203 | return cty.StringVal(str[start:end]) | ||
204 | case retType.IsTupleType(): | ||
205 | captureIdxs = captureIdxs[2:] // index 0 is the whole pattern span, which we ignore by skipping one pair | ||
206 | vals := make([]cty.Value, len(captureIdxs)/2) | ||
207 | for i := range vals { | ||
208 | start, end := captureIdxs[i*2], captureIdxs[i*2+1] | ||
209 | if start < 0 || end < 0 { | ||
210 | vals[i] = cty.NullVal(cty.String) // Did not match anything because containing group didn't match | ||
211 | continue | ||
212 | } | ||
213 | vals[i] = cty.StringVal(str[start:end]) | ||
214 | } | ||
215 | return cty.TupleVal(vals) | ||
216 | case retType.IsObjectType(): | ||
217 | captureIdxs = captureIdxs[2:] // index 0 is the whole pattern span, which we ignore by skipping one pair | ||
218 | vals := make(map[string]cty.Value, len(captureIdxs)/2) | ||
219 | names := re.SubexpNames()[1:] | ||
220 | for i, name := range names { | ||
221 | start, end := captureIdxs[i*2], captureIdxs[i*2+1] | ||
222 | if start < 0 || end < 0 { | ||
223 | vals[name] = cty.NullVal(cty.String) // Did not match anything because containing group didn't match | ||
224 | continue | ||
225 | } | ||
226 | vals[name] = cty.StringVal(str[start:end]) | ||
227 | } | ||
228 | return cty.ObjectVal(vals) | ||
229 | default: | ||
230 | // Should never happen | ||
231 | panic(fmt.Sprintf("invalid return type %#v", retType)) | ||
232 | } | ||
233 | } | ||
diff --git a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/sequence.go b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/sequence.go index e2c77c5..d3cc341 100644 --- a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/sequence.go +++ b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/sequence.go | |||
@@ -119,6 +119,75 @@ var ConcatFunc = function.New(&function.Spec{ | |||
119 | }, | 119 | }, |
120 | }) | 120 | }) |
121 | 121 | ||
122 | var RangeFunc = function.New(&function.Spec{ | ||
123 | VarParam: &function.Parameter{ | ||
124 | Name: "params", | ||
125 | Type: cty.Number, | ||
126 | }, | ||
127 | Type: function.StaticReturnType(cty.List(cty.Number)), | ||
128 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
129 | var start, end, step cty.Value | ||
130 | switch len(args) { | ||
131 | case 1: | ||
132 | if args[0].LessThan(cty.Zero).True() { | ||
133 | start, end, step = cty.Zero, args[0], cty.NumberIntVal(-1) | ||
134 | } else { | ||
135 | start, end, step = cty.Zero, args[0], cty.NumberIntVal(1) | ||
136 | } | ||
137 | case 2: | ||
138 | if args[1].LessThan(args[0]).True() { | ||
139 | start, end, step = args[0], args[1], cty.NumberIntVal(-1) | ||
140 | } else { | ||
141 | start, end, step = args[0], args[1], cty.NumberIntVal(1) | ||
142 | } | ||
143 | case 3: | ||
144 | start, end, step = args[0], args[1], args[2] | ||
145 | default: | ||
146 | return cty.NilVal, fmt.Errorf("must have one, two, or three arguments") | ||
147 | } | ||
148 | |||
149 | var vals []cty.Value | ||
150 | |||
151 | if step == cty.Zero { | ||
152 | return cty.NilVal, function.NewArgErrorf(2, "step must not be zero") | ||
153 | } | ||
154 | down := step.LessThan(cty.Zero).True() | ||
155 | |||
156 | if down { | ||
157 | if end.GreaterThan(start).True() { | ||
158 | return cty.NilVal, function.NewArgErrorf(1, "end must be less than start when step is negative") | ||
159 | } | ||
160 | } else { | ||
161 | if end.LessThan(start).True() { | ||
162 | return cty.NilVal, function.NewArgErrorf(1, "end must be greater than start when step is positive") | ||
163 | } | ||
164 | } | ||
165 | |||
166 | num := start | ||
167 | for { | ||
168 | if down { | ||
169 | if num.LessThanOrEqualTo(end).True() { | ||
170 | break | ||
171 | } | ||
172 | } else { | ||
173 | if num.GreaterThanOrEqualTo(end).True() { | ||
174 | break | ||
175 | } | ||
176 | } | ||
177 | if len(vals) >= 1024 { | ||
178 | // Artificial limit to prevent bad arguments from consuming huge amounts of memory | ||
179 | return cty.NilVal, fmt.Errorf("more than 1024 values were generated; either decrease the difference between start and end or use a smaller step") | ||
180 | } | ||
181 | vals = append(vals, num) | ||
182 | num = num.Add(step) | ||
183 | } | ||
184 | if len(vals) == 0 { | ||
185 | return cty.ListValEmpty(cty.Number), nil | ||
186 | } | ||
187 | return cty.ListVal(vals), nil | ||
188 | }, | ||
189 | }) | ||
190 | |||
122 | // Concat takes one or more sequences (lists or tuples) and returns the single | 191 | // Concat takes one or more sequences (lists or tuples) and returns the single |
123 | // sequence that results from concatenating them together in order. | 192 | // sequence that results from concatenating them together in order. |
124 | // | 193 | // |
@@ -128,3 +197,22 @@ var ConcatFunc = function.New(&function.Spec{ | |||
128 | func Concat(seqs ...cty.Value) (cty.Value, error) { | 197 | func Concat(seqs ...cty.Value) (cty.Value, error) { |
129 | return ConcatFunc.Call(seqs) | 198 | return ConcatFunc.Call(seqs) |
130 | } | 199 | } |
200 | |||
201 | // Range creates a list of numbers by starting from the given starting value, | ||
202 | // then adding the given step value until the result is greater than or | ||
203 | // equal to the given stopping value. Each intermediate result becomes an | ||
204 | // element in the resulting list. | ||
205 | // | ||
206 | // When all three parameters are set, the order is (start, end, step). If | ||
207 | // only two parameters are set, they are the start and end respectively and | ||
208 | // step defaults to 1. If only one argument is set, it gives the end value | ||
209 | // with start defaulting to 0 and step defaulting to 1. | ||
210 | // | ||
211 | // Because the resulting list must be fully buffered in memory, there is an | ||
212 | // artificial cap of 1024 elements, after which this function will return | ||
213 | // an error to avoid consuming unbounded amounts of memory. The Range function | ||
214 | // is primarily intended for creating small lists of indices to iterate over, | ||
215 | // so there should be no reason to generate huge lists with it. | ||
216 | func Range(params ...cty.Value) (cty.Value, error) { | ||
217 | return RangeFunc.Call(params) | ||
218 | } | ||