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