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  }