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