github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/hcl2/model/scope.go (about) 1 // Copyright 2016-2020, Pulumi Corporation. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package model 16 17 import ( 18 "github.com/hashicorp/hcl/v2" 19 "github.com/hashicorp/hcl/v2/hclsyntax" 20 "github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/syntax" 21 "github.com/zclconf/go-cty/cty" 22 ) 23 24 // Definition represents a single definition in a Scope. 25 type Definition interface { 26 Traversable 27 28 SyntaxNode() hclsyntax.Node 29 } 30 31 // A Keyword is a non-traversable definition that allows scope traversals to bind to arbitrary keywords. 32 type Keyword string 33 34 // Traverse attempts to traverse the keyword, and always fails. 35 func (kw Keyword) Traverse(traverser hcl.Traverser) (Traversable, hcl.Diagnostics) { 36 return DynamicType, hcl.Diagnostics{cannotTraverseKeyword(string(kw), traverser.SourceRange())} 37 } 38 39 // SyntaxNode returns the syntax node for the keyword, which is always syntax.None. 40 func (kw Keyword) SyntaxNode() hclsyntax.Node { 41 return syntax.None 42 } 43 44 // A Variable is a traversable, typed definition that represents a named value. 45 type Variable struct { 46 // The syntax node associated with the variable definition, if any. 47 Syntax hclsyntax.Node 48 49 // The name of the variable. 50 Name string 51 // The type of the variable. 52 VariableType Type 53 } 54 55 // Traverse attempts to traverse the variable's type. 56 func (v *Variable) Traverse(traverser hcl.Traverser) (Traversable, hcl.Diagnostics) { 57 return v.VariableType.Traverse(traverser) 58 } 59 60 // SyntaxNode returns the variable's syntax node or syntax.None. 61 func (v *Variable) SyntaxNode() hclsyntax.Node { 62 return syntaxOrNone(v.Syntax) 63 } 64 65 // Type returns the type of the variable. 66 func (v *Variable) Type() Type { 67 return v.VariableType 68 } 69 70 func (v *Variable) Value(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { 71 if value, hasValue := context.Variables[v.Name]; hasValue { 72 return value, nil 73 } 74 return cty.DynamicVal, nil 75 } 76 77 // A Constant is a traversable, typed definition that represents a named constant. 78 type Constant struct { 79 // The syntax node associated with the constant definition, if any. 80 Syntax hclsyntax.Node 81 82 // The name of the constant. 83 Name string 84 // The value of the constant. 85 ConstantValue cty.Value 86 87 typ Type 88 } 89 90 // Tracerse attempts to traverse the constant's value. 91 func (c *Constant) Traverse(traverser hcl.Traverser) (Traversable, hcl.Diagnostics) { 92 v, diags := traverser.TraversalStep(c.ConstantValue) 93 return &Constant{ConstantValue: v}, diags 94 } 95 96 // SyntaxNode returns the constant's syntax node or syntax.None. 97 func (c *Constant) SyntaxNode() hclsyntax.Node { 98 return syntaxOrNone(c.Syntax) 99 } 100 101 // Type returns the type of the constant. 102 func (c *Constant) Type() Type { 103 if c.typ == nil { 104 if c.ConstantValue.IsNull() { 105 c.typ = NoneType 106 } else { 107 c.typ = ctyTypeToType(c.ConstantValue.Type(), false) 108 } 109 } 110 return c.typ 111 } 112 113 func (c *Constant) Value(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { 114 return c.ConstantValue, nil 115 } 116 117 // A Scope is used to map names to definitions during expression binding. 118 // 119 // A scope has two namespaces: one that is exclusive to functions and one that contains both variables and functions. 120 // When binding a reference, only the latter is checked; when binding a function, only the former is checked. 121 type Scope struct { 122 parent *Scope 123 syntax hclsyntax.Node 124 defs map[string]Definition 125 functions map[string]*Function 126 } 127 128 // NewRootScope returns a new unparented scope associated with the given syntax node. 129 func NewRootScope(syntax hclsyntax.Node) *Scope { 130 return &Scope{ 131 syntax: syntax, 132 defs: map[string]Definition{}, 133 functions: map[string]*Function{}, 134 } 135 } 136 137 // Traverse attempts to traverse the scope using the given traverser. If the traverser is a literal string that refers 138 // to a name defined within the scope or one of its ancestors, the traversal returns the corresponding definition. 139 func (s *Scope) Traverse(traverser hcl.Traverser) (Traversable, hcl.Diagnostics) { 140 name, nameType := GetTraverserKey(traverser) 141 if nameType != StringType { 142 // TODO(pdg): return a better error here 143 return DynamicType, hcl.Diagnostics{undefinedVariable("", traverser.SourceRange())} 144 } 145 146 memberName := name.AsString() 147 member, hasMember := s.BindReference(memberName) 148 if !hasMember { 149 return DynamicType, hcl.Diagnostics{undefinedVariable(memberName, traverser.SourceRange())} 150 } 151 return member, nil 152 } 153 154 // SyntaxNode returns the syntax node associated with the scope, if any. 155 func (s *Scope) SyntaxNode() hclsyntax.Node { 156 if s != nil { 157 return syntaxOrNone(s.syntax) 158 } 159 return syntax.None 160 } 161 162 // BindReference returns the definition that corresponds to the given name, if any. Each parent scope is checked until 163 // a definition is found or no parent scope remains. 164 func (s *Scope) BindReference(name string) (Definition, bool) { 165 if s != nil { 166 if def, ok := s.defs[name]; ok { 167 return def, true 168 } 169 if s.parent != nil { 170 return s.parent.BindReference(name) 171 } 172 } 173 return nil, false 174 } 175 176 // BindFunctionReference returns the function definition that corresponds to the given name, if any. Each parent scope 177 // is checked until a definition is found or no parent scope remains. 178 func (s *Scope) BindFunctionReference(name string) (*Function, bool) { 179 if s != nil { 180 if fn, ok := s.functions[name]; ok { 181 return fn, true 182 } 183 if s.parent != nil { 184 return s.parent.BindFunctionReference(name) 185 } 186 } 187 return nil, false 188 } 189 190 // Define maps the given name to the given definition. If the name is already defined in this scope, the existing 191 // definition is not overwritten and Define returns false. 192 func (s *Scope) Define(name string, def Definition) bool { 193 if s != nil { 194 if _, hasDef := s.defs[name]; !hasDef { 195 s.defs[name] = def 196 return true 197 } 198 } 199 return false 200 } 201 202 // DefineFunction maps the given function name to the given function definition. If the function is alreadu defined in 203 // this scope, the definition is not overwritten and DefineFunction returns false. 204 func (s *Scope) DefineFunction(name string, def *Function) bool { 205 if s != nil { 206 if _, hasFunc := s.functions[name]; !hasFunc { 207 s.functions[name] = def 208 return true 209 } 210 } 211 return false 212 } 213 214 // DefineScope defines a child scope with the given name. If the name is already defined in this scope, the existing 215 // definition is not overwritten and DefineScope returns false. 216 func (s *Scope) DefineScope(name string, syntax hclsyntax.Node) (*Scope, bool) { 217 if s != nil { 218 if _, exists := s.defs[name]; !exists { 219 child := &Scope{ 220 parent: s, 221 syntax: syntax, 222 defs: map[string]Definition{}, 223 functions: map[string]*Function{}, 224 } 225 s.defs[name] = child 226 return child, true 227 } 228 } 229 return nil, false 230 } 231 232 // Push defines an anonymous child scope associated with the given syntax node. 233 func (s *Scope) Push(syntax hclsyntax.Node) *Scope { 234 return &Scope{ 235 parent: s, 236 syntax: syntax, 237 defs: map[string]Definition{}, 238 functions: map[string]*Function{}, 239 } 240 } 241 242 // Pop returns this scope's parent. 243 func (s *Scope) Pop() *Scope { 244 return s.parent 245 } 246 247 // Scopes is the interface that is used fetch the scope that should be used when binding a block or attribute. 248 type Scopes interface { 249 // GetScopesForBlock returns the Scopes that should be used when binding the given block. 250 GetScopesForBlock(block *hclsyntax.Block) (Scopes, hcl.Diagnostics) 251 // GetScopeForAttribute returns the *Scope that should be used when binding the given attribute. 252 GetScopeForAttribute(attribute *hclsyntax.Attribute) (*Scope, hcl.Diagnostics) 253 } 254 255 type staticScope struct { 256 scope *Scope 257 } 258 259 // GetScopesForBlock returns the scopes to use when binding the given block. 260 func (s staticScope) GetScopesForBlock(block *hclsyntax.Block) (Scopes, hcl.Diagnostics) { 261 return s, nil 262 } 263 264 // GetScopeForAttribute returns the scope to use when binding the given attribute. 265 func (s staticScope) GetScopeForAttribute(attribute *hclsyntax.Attribute) (*Scope, hcl.Diagnostics) { 266 return s.scope, nil 267 } 268 269 // StaticScope returns a Scopes that uses the given *Scope for all blocks and attributes. 270 func StaticScope(scope *Scope) Scopes { 271 return staticScope{scope: scope} 272 }