]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package stdlib |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "math/big" | |
6 | ||
7 | "github.com/zclconf/go-cty/cty" | |
8 | "github.com/zclconf/go-cty/cty/function" | |
9 | ) | |
10 | ||
11 | var AbsoluteFunc = function.New(&function.Spec{ | |
12 | Params: []function.Parameter{ | |
13 | { | |
14 | Name: "num", | |
15 | Type: cty.Number, | |
16 | AllowDynamicType: true, | |
17 | }, | |
18 | }, | |
19 | Type: function.StaticReturnType(cty.Number), | |
20 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | |
21 | return args[0].Absolute(), nil | |
22 | }, | |
23 | }) | |
24 | ||
25 | var AddFunc = function.New(&function.Spec{ | |
26 | Params: []function.Parameter{ | |
27 | { | |
28 | Name: "a", | |
29 | Type: cty.Number, | |
30 | AllowDynamicType: true, | |
31 | }, | |
32 | { | |
33 | Name: "b", | |
34 | Type: cty.Number, | |
35 | AllowDynamicType: true, | |
36 | }, | |
37 | }, | |
38 | Type: function.StaticReturnType(cty.Number), | |
39 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
40 | // big.Float.Add can panic if the input values are opposing infinities, | |
41 | // so we must catch that here in order to remain within | |
42 | // the cty Function abstraction. | |
43 | defer func() { | |
44 | if r := recover(); r != nil { | |
45 | if _, ok := r.(big.ErrNaN); ok { | |
46 | ret = cty.NilVal | |
47 | err = fmt.Errorf("can't compute sum of opposing infinities") | |
48 | } else { | |
49 | // not a panic we recognize | |
50 | panic(r) | |
51 | } | |
52 | } | |
53 | }() | |
54 | return args[0].Add(args[1]), nil | |
55 | }, | |
56 | }) | |
57 | ||
58 | var SubtractFunc = function.New(&function.Spec{ | |
59 | Params: []function.Parameter{ | |
60 | { | |
61 | Name: "a", | |
62 | Type: cty.Number, | |
63 | AllowDynamicType: true, | |
64 | }, | |
65 | { | |
66 | Name: "b", | |
67 | Type: cty.Number, | |
68 | AllowDynamicType: true, | |
69 | }, | |
70 | }, | |
71 | Type: function.StaticReturnType(cty.Number), | |
72 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
73 | // big.Float.Sub can panic if the input values are infinities, | |
74 | // so we must catch that here in order to remain within | |
75 | // the cty Function abstraction. | |
76 | defer func() { | |
77 | if r := recover(); r != nil { | |
78 | if _, ok := r.(big.ErrNaN); ok { | |
79 | ret = cty.NilVal | |
80 | err = fmt.Errorf("can't subtract infinity from itself") | |
81 | } else { | |
82 | // not a panic we recognize | |
83 | panic(r) | |
84 | } | |
85 | } | |
86 | }() | |
87 | return args[0].Subtract(args[1]), nil | |
88 | }, | |
89 | }) | |
90 | ||
91 | var MultiplyFunc = function.New(&function.Spec{ | |
92 | Params: []function.Parameter{ | |
93 | { | |
94 | Name: "a", | |
95 | Type: cty.Number, | |
96 | AllowDynamicType: true, | |
97 | }, | |
98 | { | |
99 | Name: "b", | |
100 | Type: cty.Number, | |
101 | AllowDynamicType: true, | |
102 | }, | |
103 | }, | |
104 | Type: function.StaticReturnType(cty.Number), | |
105 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
106 | // big.Float.Mul can panic if the input values are both zero or both | |
107 | // infinity, so we must catch that here in order to remain within | |
108 | // the cty Function abstraction. | |
109 | defer func() { | |
110 | if r := recover(); r != nil { | |
111 | if _, ok := r.(big.ErrNaN); ok { | |
112 | ret = cty.NilVal | |
113 | err = fmt.Errorf("can't multiply zero by infinity") | |
114 | } else { | |
115 | // not a panic we recognize | |
116 | panic(r) | |
117 | } | |
118 | } | |
119 | }() | |
120 | ||
121 | return args[0].Multiply(args[1]), nil | |
122 | }, | |
123 | }) | |
124 | ||
125 | var DivideFunc = function.New(&function.Spec{ | |
126 | Params: []function.Parameter{ | |
127 | { | |
128 | Name: "a", | |
129 | Type: cty.Number, | |
130 | AllowDynamicType: true, | |
131 | }, | |
132 | { | |
133 | Name: "b", | |
134 | Type: cty.Number, | |
135 | AllowDynamicType: true, | |
136 | }, | |
137 | }, | |
138 | Type: function.StaticReturnType(cty.Number), | |
139 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
140 | // big.Float.Quo can panic if the input values are both zero or both | |
141 | // infinity, so we must catch that here in order to remain within | |
142 | // the cty Function abstraction. | |
143 | defer func() { | |
144 | if r := recover(); r != nil { | |
145 | if _, ok := r.(big.ErrNaN); ok { | |
146 | ret = cty.NilVal | |
147 | err = fmt.Errorf("can't divide zero by zero or infinity by infinity") | |
148 | } else { | |
149 | // not a panic we recognize | |
150 | panic(r) | |
151 | } | |
152 | } | |
153 | }() | |
154 | ||
155 | return args[0].Divide(args[1]), nil | |
156 | }, | |
157 | }) | |
158 | ||
159 | var ModuloFunc = function.New(&function.Spec{ | |
160 | Params: []function.Parameter{ | |
161 | { | |
162 | Name: "a", | |
163 | Type: cty.Number, | |
164 | AllowDynamicType: true, | |
165 | }, | |
166 | { | |
167 | Name: "b", | |
168 | Type: cty.Number, | |
169 | AllowDynamicType: true, | |
170 | }, | |
171 | }, | |
172 | Type: function.StaticReturnType(cty.Number), | |
173 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
174 | // big.Float.Mul can panic if the input values are both zero or both | |
175 | // infinity, so we must catch that here in order to remain within | |
176 | // the cty Function abstraction. | |
177 | defer func() { | |
178 | if r := recover(); r != nil { | |
179 | if _, ok := r.(big.ErrNaN); ok { | |
180 | ret = cty.NilVal | |
181 | err = fmt.Errorf("can't use modulo with zero and infinity") | |
182 | } else { | |
183 | // not a panic we recognize | |
184 | panic(r) | |
185 | } | |
186 | } | |
187 | }() | |
188 | ||
189 | return args[0].Modulo(args[1]), nil | |
190 | }, | |
191 | }) | |
192 | ||
193 | var GreaterThanFunc = function.New(&function.Spec{ | |
194 | Params: []function.Parameter{ | |
195 | { | |
196 | Name: "a", | |
197 | Type: cty.Number, | |
198 | AllowDynamicType: true, | |
199 | }, | |
200 | { | |
201 | Name: "b", | |
202 | Type: cty.Number, | |
203 | AllowDynamicType: true, | |
204 | }, | |
205 | }, | |
206 | Type: function.StaticReturnType(cty.Bool), | |
207 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
208 | return args[0].GreaterThan(args[1]), nil | |
209 | }, | |
210 | }) | |
211 | ||
212 | var GreaterThanOrEqualToFunc = function.New(&function.Spec{ | |
213 | Params: []function.Parameter{ | |
214 | { | |
215 | Name: "a", | |
216 | Type: cty.Number, | |
217 | AllowDynamicType: true, | |
218 | }, | |
219 | { | |
220 | Name: "b", | |
221 | Type: cty.Number, | |
222 | AllowDynamicType: true, | |
223 | }, | |
224 | }, | |
225 | Type: function.StaticReturnType(cty.Bool), | |
226 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
227 | return args[0].GreaterThanOrEqualTo(args[1]), nil | |
228 | }, | |
229 | }) | |
230 | ||
231 | var LessThanFunc = function.New(&function.Spec{ | |
232 | Params: []function.Parameter{ | |
233 | { | |
234 | Name: "a", | |
235 | Type: cty.Number, | |
236 | AllowDynamicType: true, | |
237 | }, | |
238 | { | |
239 | Name: "b", | |
240 | Type: cty.Number, | |
241 | AllowDynamicType: true, | |
242 | }, | |
243 | }, | |
244 | Type: function.StaticReturnType(cty.Bool), | |
245 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
246 | return args[0].LessThan(args[1]), nil | |
247 | }, | |
248 | }) | |
249 | ||
250 | var LessThanOrEqualToFunc = function.New(&function.Spec{ | |
251 | Params: []function.Parameter{ | |
252 | { | |
253 | Name: "a", | |
254 | Type: cty.Number, | |
255 | AllowDynamicType: true, | |
256 | }, | |
257 | { | |
258 | Name: "b", | |
259 | Type: cty.Number, | |
260 | AllowDynamicType: true, | |
261 | }, | |
262 | }, | |
263 | Type: function.StaticReturnType(cty.Bool), | |
264 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
265 | return args[0].LessThanOrEqualTo(args[1]), nil | |
266 | }, | |
267 | }) | |
268 | ||
269 | var NegateFunc = function.New(&function.Spec{ | |
270 | Params: []function.Parameter{ | |
271 | { | |
272 | Name: "num", | |
273 | Type: cty.Number, | |
274 | AllowDynamicType: true, | |
275 | }, | |
276 | }, | |
277 | Type: function.StaticReturnType(cty.Number), | |
278 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | |
279 | return args[0].Negate(), nil | |
280 | }, | |
281 | }) | |
282 | ||
283 | var MinFunc = function.New(&function.Spec{ | |
284 | Params: []function.Parameter{}, | |
285 | VarParam: &function.Parameter{ | |
286 | Name: "numbers", | |
287 | Type: cty.Number, | |
288 | AllowDynamicType: true, | |
289 | }, | |
290 | Type: function.StaticReturnType(cty.Number), | |
291 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | |
292 | if len(args) == 0 { | |
293 | return cty.NilVal, fmt.Errorf("must pass at least one number") | |
294 | } | |
295 | ||
296 | min := cty.PositiveInfinity | |
297 | for _, num := range args { | |
298 | if num.LessThan(min).True() { | |
299 | min = num | |
300 | } | |
301 | } | |
302 | ||
303 | return min, nil | |
304 | }, | |
305 | }) | |
306 | ||
307 | var MaxFunc = function.New(&function.Spec{ | |
308 | Params: []function.Parameter{}, | |
309 | VarParam: &function.Parameter{ | |
310 | Name: "numbers", | |
311 | Type: cty.Number, | |
312 | AllowDynamicType: true, | |
313 | }, | |
314 | Type: function.StaticReturnType(cty.Number), | |
315 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | |
316 | if len(args) == 0 { | |
317 | return cty.NilVal, fmt.Errorf("must pass at least one number") | |
318 | } | |
319 | ||
320 | max := cty.NegativeInfinity | |
321 | for _, num := range args { | |
322 | if num.GreaterThan(max).True() { | |
323 | max = num | |
324 | } | |
325 | } | |
326 | ||
327 | return max, nil | |
328 | }, | |
329 | }) | |
330 | ||
331 | var IntFunc = function.New(&function.Spec{ | |
332 | Params: []function.Parameter{ | |
333 | { | |
334 | Name: "num", | |
335 | Type: cty.Number, | |
336 | AllowDynamicType: true, | |
337 | }, | |
338 | }, | |
339 | Type: function.StaticReturnType(cty.Number), | |
340 | Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | |
341 | bf := args[0].AsBigFloat() | |
342 | if bf.IsInt() { | |
343 | return args[0], nil | |
344 | } | |
345 | bi, _ := bf.Int(nil) | |
346 | bf = (&big.Float{}).SetInt(bi) | |
347 | return cty.NumberVal(bf), nil | |
348 | }, | |
349 | }) | |
350 | ||
351 | // Absolute returns the magnitude of the given number, without its sign. | |
352 | // That is, it turns negative values into positive values. | |
353 | func Absolute(num cty.Value) (cty.Value, error) { | |
354 | return AbsoluteFunc.Call([]cty.Value{num}) | |
355 | } | |
356 | ||
357 | // Add returns the sum of the two given numbers. | |
358 | func Add(a cty.Value, b cty.Value) (cty.Value, error) { | |
359 | return AddFunc.Call([]cty.Value{a, b}) | |
360 | } | |
361 | ||
362 | // Subtract returns the difference between the two given numbers. | |
363 | func Subtract(a cty.Value, b cty.Value) (cty.Value, error) { | |
364 | return SubtractFunc.Call([]cty.Value{a, b}) | |
365 | } | |
366 | ||
367 | // Multiply returns the product of the two given numbers. | |
368 | func Multiply(a cty.Value, b cty.Value) (cty.Value, error) { | |
369 | return MultiplyFunc.Call([]cty.Value{a, b}) | |
370 | } | |
371 | ||
372 | // Divide returns a divided by b, where both a and b are numbers. | |
373 | func Divide(a cty.Value, b cty.Value) (cty.Value, error) { | |
374 | return DivideFunc.Call([]cty.Value{a, b}) | |
375 | } | |
376 | ||
377 | // Negate returns the given number multipled by -1. | |
378 | func Negate(num cty.Value) (cty.Value, error) { | |
379 | return NegateFunc.Call([]cty.Value{num}) | |
380 | } | |
381 | ||
382 | // LessThan returns true if a is less than b. | |
383 | func LessThan(a cty.Value, b cty.Value) (cty.Value, error) { | |
384 | return LessThanFunc.Call([]cty.Value{a, b}) | |
385 | } | |
386 | ||
387 | // LessThanOrEqualTo returns true if a is less than b. | |
388 | func LessThanOrEqualTo(a cty.Value, b cty.Value) (cty.Value, error) { | |
389 | return LessThanOrEqualToFunc.Call([]cty.Value{a, b}) | |
390 | } | |
391 | ||
392 | // GreaterThan returns true if a is less than b. | |
393 | func GreaterThan(a cty.Value, b cty.Value) (cty.Value, error) { | |
394 | return GreaterThanFunc.Call([]cty.Value{a, b}) | |
395 | } | |
396 | ||
397 | // GreaterThanOrEqualTo returns true if a is less than b. | |
398 | func GreaterThanOrEqualTo(a cty.Value, b cty.Value) (cty.Value, error) { | |
399 | return GreaterThanOrEqualToFunc.Call([]cty.Value{a, b}) | |
400 | } | |
401 | ||
402 | // Modulo returns the remainder of a divided by b under integer division, | |
403 | // where both a and b are numbers. | |
404 | func Modulo(a cty.Value, b cty.Value) (cty.Value, error) { | |
405 | return ModuloFunc.Call([]cty.Value{a, b}) | |
406 | } | |
407 | ||
408 | // Min returns the minimum number from the given numbers. | |
409 | func Min(numbers ...cty.Value) (cty.Value, error) { | |
410 | return MinFunc.Call(numbers) | |
411 | } | |
412 | ||
413 | // Max returns the maximum number from the given numbers. | |
414 | func Max(numbers ...cty.Value) (cty.Value, error) { | |
415 | return MaxFunc.Call(numbers) | |
416 | } | |
417 | ||
418 | // Int removes the fractional component of the given number returning an | |
419 | // integer representing the whole number component, rounding towards zero. | |
420 | // For example, -1.5 becomes -1. | |
421 | // | |
422 | // If an infinity is passed to Int, an error is returned. | |
423 | func Int(num cty.Value) (cty.Value, error) { | |
424 | if num == cty.PositiveInfinity || num == cty.NegativeInfinity { | |
425 | return cty.NilVal, fmt.Errorf("can't truncate infinity to an integer") | |
426 | } | |
427 | return IntFunc.Call([]cty.Value{num}) | |
428 | } |