]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
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 | } |