1package hil
2
3import (
4	"bytes"
5	"fmt"
6	"sync"
7
8	"github.com/hashicorp/hil/ast"
9)
10
11// EvalConfig is the configuration for evaluating.
12type EvalConfig struct {
13	// GlobalScope is the global scope of execution for evaluation.
14	GlobalScope *ast.BasicScope
15
16	// SemanticChecks is a list of additional semantic checks that will be run
17	// on the tree prior to evaluating it. The type checker, identifier checker,
18	// etc. will be run before these automatically.
19	SemanticChecks []SemanticChecker
20}
21
22// SemanticChecker is the type that must be implemented to do a
23// semantic check on an AST tree. This will be called with the root node.
24type SemanticChecker func(ast.Node) error
25
26// EvalType represents the type of the output returned from a HIL
27// evaluation.
28type EvalType uint32
29
30const (
31	TypeInvalid EvalType = 0
32	TypeString  EvalType = 1 << iota
33	TypeList
34	TypeMap
35)
36
37//go:generate stringer -type=EvalType
38
39// EvaluationResult is a struct returned from the hil.Eval function,
40// representing the result of an interpolation. Results are returned in their
41// "natural" Go structure rather than in terms of the HIL AST.  For the types
42// currently implemented, this means that the Value field can be interpreted as
43// the following Go types:
44//     TypeInvalid: undefined
45//     TypeString:  string
46//     TypeList:    []interface{}
47//     TypeMap:     map[string]interface{}
48type EvaluationResult struct {
49	Type  EvalType
50	Value interface{}
51}
52
53// InvalidResult is a structure representing the result of a HIL interpolation
54// which has invalid syntax, missing variables, or some other type of error.
55// The error is described out of band in the accompanying error return value.
56var InvalidResult = EvaluationResult{Type: TypeInvalid, Value: nil}
57
58func Eval(root ast.Node, config *EvalConfig) (EvaluationResult, error) {
59	output, outputType, err := internalEval(root, config)
60	if err != nil {
61		return InvalidResult, err
62	}
63
64	switch outputType {
65	case ast.TypeList:
66		val, err := VariableToInterface(ast.Variable{
67			Type:  ast.TypeList,
68			Value: output,
69		})
70		return EvaluationResult{
71			Type:  TypeList,
72			Value: val,
73		}, err
74	case ast.TypeMap:
75		val, err := VariableToInterface(ast.Variable{
76			Type:  ast.TypeMap,
77			Value: output,
78		})
79		return EvaluationResult{
80			Type: TypeMap,
81			Value: val,
82		}, err
83	case ast.TypeString:
84		return EvaluationResult{
85			Type:  TypeString,
86			Value: output,
87		}, nil
88	default:
89		return InvalidResult, fmt.Errorf("unknown type %s as interpolation output", outputType)
90	}
91}
92
93// Eval evaluates the given AST tree and returns its output value, the type
94// of the output, and any error that occurred.
95func internalEval(root ast.Node, config *EvalConfig) (interface{}, ast.Type, error) {
96	// Copy the scope so we can add our builtins
97	if config == nil {
98		config = new(EvalConfig)
99	}
100	scope := registerBuiltins(config.GlobalScope)
101	implicitMap := map[ast.Type]map[ast.Type]string{
102		ast.TypeFloat: {
103			ast.TypeInt:    "__builtin_FloatToInt",
104			ast.TypeString: "__builtin_FloatToString",
105		},
106		ast.TypeInt: {
107			ast.TypeFloat:  "__builtin_IntToFloat",
108			ast.TypeString: "__builtin_IntToString",
109		},
110		ast.TypeString: {
111			ast.TypeInt:   "__builtin_StringToInt",
112			ast.TypeFloat: "__builtin_StringToFloat",
113		},
114	}
115
116	// Build our own semantic checks that we always run
117	tv := &TypeCheck{Scope: scope, Implicit: implicitMap}
118	ic := &IdentifierCheck{Scope: scope}
119
120	// Build up the semantic checks for execution
121	checks := make(
122		[]SemanticChecker,
123		len(config.SemanticChecks),
124		len(config.SemanticChecks)+2)
125	copy(checks, config.SemanticChecks)
126	checks = append(checks, ic.Visit)
127	checks = append(checks, tv.Visit)
128
129	// Run the semantic checks
130	for _, check := range checks {
131		if err := check(root); err != nil {
132			return nil, ast.TypeInvalid, err
133		}
134	}
135
136	// Execute
137	v := &evalVisitor{Scope: scope}
138	return v.Visit(root)
139}
140
141// EvalNode is the interface that must be implemented by any ast.Node
142// to support evaluation. This will be called in visitor pattern order.
143// The result of each call to Eval is automatically pushed onto the
144// stack as a LiteralNode. Pop elements off the stack to get child
145// values.
146type EvalNode interface {
147	Eval(ast.Scope, *ast.Stack) (interface{}, ast.Type, error)
148}
149
150type evalVisitor struct {
151	Scope ast.Scope
152	Stack ast.Stack
153
154	err  error
155	lock sync.Mutex
156}
157
158func (v *evalVisitor) Visit(root ast.Node) (interface{}, ast.Type, error) {
159	// Run the actual visitor pattern
160	root.Accept(v.visit)
161
162	// Get our result and clear out everything else
163	var result *ast.LiteralNode
164	if v.Stack.Len() > 0 {
165		result = v.Stack.Pop().(*ast.LiteralNode)
166	} else {
167		result = new(ast.LiteralNode)
168	}
169	resultErr := v.err
170
171	// Clear everything else so we aren't just dangling
172	v.Stack.Reset()
173	v.err = nil
174
175	t, err := result.Type(v.Scope)
176	if err != nil {
177		return nil, ast.TypeInvalid, err
178	}
179
180	return result.Value, t, resultErr
181}
182
183func (v *evalVisitor) visit(raw ast.Node) ast.Node {
184	if v.err != nil {
185		return raw
186	}
187
188	en, err := evalNode(raw)
189	if err != nil {
190		v.err = err
191		return raw
192	}
193
194	out, outType, err := en.Eval(v.Scope, &v.Stack)
195	if err != nil {
196		v.err = err
197		return raw
198	}
199
200	v.Stack.Push(&ast.LiteralNode{
201		Value: out,
202		Typex: outType,
203	})
204	return raw
205}
206
207// evalNode is a private function that returns an EvalNode for built-in
208// types as well as any other EvalNode implementations.
209func evalNode(raw ast.Node) (EvalNode, error) {
210	switch n := raw.(type) {
211	case *ast.Index:
212		return &evalIndex{n}, nil
213	case *ast.Call:
214		return &evalCall{n}, nil
215	case *ast.Output:
216		return &evalOutput{n}, nil
217	case *ast.LiteralNode:
218		return &evalLiteralNode{n}, nil
219	case *ast.VariableAccess:
220		return &evalVariableAccess{n}, nil
221	default:
222		en, ok := n.(EvalNode)
223		if !ok {
224			return nil, fmt.Errorf("node doesn't support evaluation: %#v", raw)
225		}
226
227		return en, nil
228	}
229}
230
231type evalCall struct{ *ast.Call }
232
233func (v *evalCall) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) {
234	// Look up the function in the map
235	function, ok := s.LookupFunc(v.Func)
236	if !ok {
237		return nil, ast.TypeInvalid, fmt.Errorf(
238			"unknown function called: %s", v.Func)
239	}
240
241	// The arguments are on the stack in reverse order, so pop them off.
242	args := make([]interface{}, len(v.Args))
243	for i, _ := range v.Args {
244		node := stack.Pop().(*ast.LiteralNode)
245		args[len(v.Args)-1-i] = node.Value
246	}
247
248	// Call the function
249	result, err := function.Callback(args)
250	if err != nil {
251		return nil, ast.TypeInvalid, fmt.Errorf("%s: %s", v.Func, err)
252	}
253
254	return result, function.ReturnType, nil
255}
256
257type evalIndex struct{ *ast.Index }
258
259func (v *evalIndex) Eval(scope ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) {
260	evalVarAccess, err := evalNode(v.Target)
261	if err != nil {
262		return nil, ast.TypeInvalid, err
263	}
264	target, targetType, err := evalVarAccess.Eval(scope, stack)
265
266	evalKey, err := evalNode(v.Key)
267	if err != nil {
268		return nil, ast.TypeInvalid, err
269	}
270
271	key, keyType, err := evalKey.Eval(scope, stack)
272	if err != nil {
273		return nil, ast.TypeInvalid, err
274	}
275
276	variableName := v.Index.Target.(*ast.VariableAccess).Name
277
278	switch targetType {
279	case ast.TypeList:
280		if keyType != ast.TypeInt {
281			return nil, ast.TypeInvalid, fmt.Errorf("key for indexing list %q must be an int, is %s", variableName, keyType)
282		}
283
284		return v.evalListIndex(variableName, target, key)
285	case ast.TypeMap:
286		if keyType != ast.TypeString {
287			return nil, ast.TypeInvalid, fmt.Errorf("key for indexing map %q must be a string, is %s", variableName, keyType)
288		}
289
290		return v.evalMapIndex(variableName, target, key)
291	default:
292		return nil, ast.TypeInvalid, fmt.Errorf("target %q for indexing must be ast.TypeList or ast.TypeMap, is %s", variableName, targetType)
293	}
294}
295
296func (v *evalIndex) evalListIndex(variableName string, target interface{}, key interface{}) (interface{}, ast.Type, error) {
297	// We assume type checking was already done and we can assume that target
298	// is a list and key is an int
299	list, ok := target.([]ast.Variable)
300	if !ok {
301		return nil, ast.TypeInvalid, fmt.Errorf("cannot cast target to []Variable")
302	}
303
304	keyInt, ok := key.(int)
305	if !ok {
306		return nil, ast.TypeInvalid, fmt.Errorf("cannot cast key to int")
307	}
308
309	if len(list) == 0 {
310		return nil, ast.TypeInvalid, fmt.Errorf("list is empty")
311	}
312
313	if keyInt < 0 || len(list) < keyInt+1 {
314		return nil, ast.TypeInvalid, fmt.Errorf("index %d out of range for list %s (max %d)", keyInt, variableName, len(list))
315	}
316
317	returnVal := list[keyInt].Value
318	returnType := list[keyInt].Type
319
320	return returnVal, returnType, nil
321}
322
323func (v *evalIndex) evalMapIndex(variableName string, target interface{}, key interface{}) (interface{}, ast.Type, error) {
324	// We assume type checking was already done and we can assume that target
325	// is a map and key is a string
326	vmap, ok := target.(map[string]ast.Variable)
327	if !ok {
328		return nil, ast.TypeInvalid, fmt.Errorf("cannot cast target to map[string]Variable")
329	}
330
331	keyString, ok := key.(string)
332	if !ok {
333		return nil, ast.TypeInvalid, fmt.Errorf("cannot cast key to string")
334	}
335
336	if len(vmap) == 0 {
337		return nil, ast.TypeInvalid, fmt.Errorf("map is empty")
338	}
339
340	value, ok := vmap[keyString]
341	if !ok {
342		return nil, ast.TypeInvalid, fmt.Errorf("key %q does not exist in map %s", keyString, variableName)
343	}
344
345	return value.Value, value.Type, nil
346}
347
348type evalOutput struct{ *ast.Output }
349
350func (v *evalOutput) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) {
351	// The expressions should all be on the stack in reverse
352	// order. So pop them off, reverse their order, and concatenate.
353	nodes := make([]*ast.LiteralNode, 0, len(v.Exprs))
354	for range v.Exprs {
355		nodes = append(nodes, stack.Pop().(*ast.LiteralNode))
356	}
357
358	// Special case the single list and map
359	if len(nodes) == 1 && nodes[0].Typex == ast.TypeList {
360		return nodes[0].Value, ast.TypeList, nil
361	}
362	if len(nodes) == 1 && nodes[0].Typex == ast.TypeMap {
363		return nodes[0].Value, ast.TypeMap, nil
364	}
365
366	// Otherwise concatenate the strings
367	var buf bytes.Buffer
368	for i := len(nodes) - 1; i >= 0; i-- {
369		buf.WriteString(nodes[i].Value.(string))
370	}
371
372	return buf.String(), ast.TypeString, nil
373}
374
375type evalLiteralNode struct{ *ast.LiteralNode }
376
377func (v *evalLiteralNode) Eval(ast.Scope, *ast.Stack) (interface{}, ast.Type, error) {
378	return v.Value, v.Typex, nil
379}
380
381type evalVariableAccess struct{ *ast.VariableAccess }
382
383func (v *evalVariableAccess) Eval(scope ast.Scope, _ *ast.Stack) (interface{}, ast.Type, error) {
384	// Look up the variable in the map
385	variable, ok := scope.LookupVar(v.Name)
386	if !ok {
387		return nil, ast.TypeInvalid, fmt.Errorf(
388			"unknown variable accessed: %s", v.Name)
389	}
390
391	return variable.Value, variable.Type, nil
392}
393