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/check_types.go | |
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/check_types.go')
-rw-r--r-- | vendor/github.com/hashicorp/hil/check_types.go | 668 |
1 files changed, 668 insertions, 0 deletions
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 | } | ||