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