github.com/franklinhu/terraform@v0.6.9-0.20151202232446-81f7fb1e6f9e/config/lang/eval.go (about) 1 package lang 2 3 import ( 4 "bytes" 5 "fmt" 6 "sync" 7 8 "github.com/hashicorp/terraform/config/lang/ast" 9 ) 10 11 // EvalConfig is the configuration for evaluating. 12 type 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. 24 type SemanticChecker func(ast.Node) error 25 26 // Eval evaluates the given AST tree and returns its output value, the type 27 // of the output, and any error that occurred. 28 func Eval(root ast.Node, config *EvalConfig) (interface{}, ast.Type, error) { 29 // Copy the scope so we can add our builtins 30 if config == nil { 31 config = new(EvalConfig) 32 } 33 scope := registerBuiltins(config.GlobalScope) 34 implicitMap := map[ast.Type]map[ast.Type]string{ 35 ast.TypeFloat: { 36 ast.TypeInt: "__builtin_FloatToInt", 37 ast.TypeString: "__builtin_FloatToString", 38 }, 39 ast.TypeInt: { 40 ast.TypeFloat: "__builtin_IntToFloat", 41 ast.TypeString: "__builtin_IntToString", 42 }, 43 ast.TypeString: { 44 ast.TypeInt: "__builtin_StringToInt", 45 }, 46 } 47 48 // Build our own semantic checks that we always run 49 tv := &TypeCheck{Scope: scope, Implicit: implicitMap} 50 ic := &IdentifierCheck{Scope: scope} 51 52 // Build up the semantic checks for execution 53 checks := make( 54 []SemanticChecker, 55 len(config.SemanticChecks), 56 len(config.SemanticChecks)+2) 57 copy(checks, config.SemanticChecks) 58 checks = append(checks, ic.Visit) 59 checks = append(checks, tv.Visit) 60 61 // Run the semantic checks 62 for _, check := range checks { 63 if err := check(root); err != nil { 64 return nil, ast.TypeInvalid, err 65 } 66 } 67 68 // Execute 69 v := &evalVisitor{Scope: scope} 70 return v.Visit(root) 71 } 72 73 // EvalNode is the interface that must be implemented by any ast.Node 74 // to support evaluation. This will be called in visitor pattern order. 75 // The result of each call to Eval is automatically pushed onto the 76 // stack as a LiteralNode. Pop elements off the stack to get child 77 // values. 78 type EvalNode interface { 79 Eval(ast.Scope, *ast.Stack) (interface{}, ast.Type, error) 80 } 81 82 type evalVisitor struct { 83 Scope ast.Scope 84 Stack ast.Stack 85 86 err error 87 lock sync.Mutex 88 } 89 90 func (v *evalVisitor) Visit(root ast.Node) (interface{}, ast.Type, error) { 91 // Run the actual visitor pattern 92 root.Accept(v.visit) 93 94 // Get our result and clear out everything else 95 var result *ast.LiteralNode 96 if v.Stack.Len() > 0 { 97 result = v.Stack.Pop().(*ast.LiteralNode) 98 } else { 99 result = new(ast.LiteralNode) 100 } 101 resultErr := v.err 102 103 // Clear everything else so we aren't just dangling 104 v.Stack.Reset() 105 v.err = nil 106 107 t, err := result.Type(v.Scope) 108 if err != nil { 109 return nil, ast.TypeInvalid, err 110 } 111 112 return result.Value, t, resultErr 113 } 114 115 func (v *evalVisitor) visit(raw ast.Node) ast.Node { 116 if v.err != nil { 117 return raw 118 } 119 120 en, err := evalNode(raw) 121 if err != nil { 122 v.err = err 123 return raw 124 } 125 126 out, outType, err := en.Eval(v.Scope, &v.Stack) 127 if err != nil { 128 v.err = err 129 return raw 130 } 131 132 v.Stack.Push(&ast.LiteralNode{ 133 Value: out, 134 Typex: outType, 135 }) 136 return raw 137 } 138 139 // evalNode is a private function that returns an EvalNode for built-in 140 // types as well as any other EvalNode implementations. 141 func evalNode(raw ast.Node) (EvalNode, error) { 142 switch n := raw.(type) { 143 case *ast.Call: 144 return &evalCall{n}, nil 145 case *ast.Concat: 146 return &evalConcat{n}, nil 147 case *ast.LiteralNode: 148 return &evalLiteralNode{n}, nil 149 case *ast.VariableAccess: 150 return &evalVariableAccess{n}, nil 151 default: 152 en, ok := n.(EvalNode) 153 if !ok { 154 return nil, fmt.Errorf("node doesn't support evaluation: %#v", raw) 155 } 156 157 return en, nil 158 } 159 } 160 161 type evalCall struct{ *ast.Call } 162 163 func (v *evalCall) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) { 164 // Look up the function in the map 165 function, ok := s.LookupFunc(v.Func) 166 if !ok { 167 return nil, ast.TypeInvalid, fmt.Errorf( 168 "unknown function called: %s", v.Func) 169 } 170 171 // The arguments are on the stack in reverse order, so pop them off. 172 args := make([]interface{}, len(v.Args)) 173 for i, _ := range v.Args { 174 node := stack.Pop().(*ast.LiteralNode) 175 args[len(v.Args)-1-i] = node.Value 176 } 177 178 // Call the function 179 result, err := function.Callback(args) 180 if err != nil { 181 return nil, ast.TypeInvalid, fmt.Errorf("%s: %s", v.Func, err) 182 } 183 184 return result, function.ReturnType, nil 185 } 186 187 type evalConcat struct{ *ast.Concat } 188 189 func (v *evalConcat) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) { 190 // The expressions should all be on the stack in reverse 191 // order. So pop them off, reverse their order, and concatenate. 192 nodes := make([]*ast.LiteralNode, 0, len(v.Exprs)) 193 for range v.Exprs { 194 nodes = append(nodes, stack.Pop().(*ast.LiteralNode)) 195 } 196 197 var buf bytes.Buffer 198 for i := len(nodes) - 1; i >= 0; i-- { 199 buf.WriteString(nodes[i].Value.(string)) 200 } 201 202 return buf.String(), ast.TypeString, nil 203 } 204 205 type evalLiteralNode struct{ *ast.LiteralNode } 206 207 func (v *evalLiteralNode) Eval(ast.Scope, *ast.Stack) (interface{}, ast.Type, error) { 208 return v.Value, v.Typex, nil 209 } 210 211 type evalVariableAccess struct{ *ast.VariableAccess } 212 213 func (v *evalVariableAccess) Eval(scope ast.Scope, _ *ast.Stack) (interface{}, ast.Type, error) { 214 // Look up the variable in the map 215 variable, ok := scope.LookupVar(v.Name) 216 if !ok { 217 return nil, ast.TypeInvalid, fmt.Errorf( 218 "unknown variable accessed: %s", v.Name) 219 } 220 221 return variable.Value, variable.Type, nil 222 }