44 "fmt"
55 "os"
66 "path/filepath"
7- "strings"
87
98 "github.com/agext/levenshtein"
109 "github.com/hashicorp/hcl/v2"
@@ -21,64 +20,11 @@ type ContextMeta struct {
2120 OriginalWorkingDir string
2221}
2322
24- type CallStack struct {
25- addrs map [string ]addrs.Reference
26- stack []string
27- }
28-
29- func NewCallStack () * CallStack {
30- return & CallStack {
31- addrs : make (map [string ]addrs.Reference ),
32- stack : make ([]string , 0 ),
33- }
34- }
35-
36- func (g * CallStack ) Push (addr addrs.Reference ) hcl.Diagnostics {
37- g .stack = append (g .stack , addr .Subject .String ())
38-
39- if _ , exists := g .addrs [addr .Subject .String ()]; exists {
40- return hcl.Diagnostics {
41- {
42- Severity : hcl .DiagError ,
43- Summary : "circular reference found" ,
44- Detail : g .String (),
45- Subject : addr .SourceRange .Ptr (),
46- },
47- }
48- }
49- g .addrs [addr .Subject .String ()] = addr
50- return hcl.Diagnostics {}
51- }
52-
53- func (g * CallStack ) Pop () {
54- if g .Empty () {
55- panic ("cannot pop from empty stack" )
56- }
57-
58- addr := g .stack [len (g .stack )- 1 ]
59- g .stack = g .stack [:len (g .stack )- 1 ]
60- delete (g .addrs , addr )
61- }
62-
63- func (g * CallStack ) String () string {
64- return strings .Join (g .stack , " -> " )
65- }
66-
67- func (g * CallStack ) Empty () bool {
68- return len (g .stack ) == 0
69- }
70-
71- func (g * CallStack ) Clear () {
72- g .addrs = make (map [string ]addrs.Reference )
73- g .stack = make ([]string , 0 )
74- }
75-
7623type Evaluator struct {
7724 Meta * ContextMeta
7825 ModulePath addrs.ModuleInstance
7926 Config * Config
8027 VariableValues map [string ]map [string ]cty.Value
81- CallStack * CallStack
8228}
8329
8430// EvaluateExpr takes the given HCL expression and evaluates it to produce a value.
@@ -101,18 +47,27 @@ func (e *Evaluator) ExpandBlock(body hcl.Body, schema *hclext.BodySchema) (hcl.B
10147 return e .scope ().ExpandBlock (body , schema )
10248}
10349
50+ // scope creates a new evaluation scope.
51+ // The difference with Evaluator is that each evaluation is independent
52+ // and is not shared between goroutines.
10453func (e * Evaluator ) scope () * lang.Scope {
105- return & lang.Scope {
106- Data : & evaluationData {
107- Evaluator : e ,
108- ModulePath : e .ModulePath ,
109- },
54+ scope := & lang.Scope {CallStack : lang .NewCallStack ()}
55+ scope .Data = & evaluationData {
56+ Scope : scope ,
57+ Meta : e .Meta ,
58+ ModulePath : e .ModulePath ,
59+ Config : e .Config ,
60+ VariableValues : e .VariableValues ,
11061 }
62+ return scope
11163}
11264
11365type evaluationData struct {
114- Evaluator * Evaluator
115- ModulePath addrs.ModuleInstance
66+ Scope * lang.Scope
67+ Meta * ContextMeta
68+ ModulePath addrs.ModuleInstance
69+ Config * Config
70+ VariableValues map [string ]map [string ]cty.Value
11671}
11772
11873var _ lang.Data = (* evaluationData )(nil )
@@ -142,7 +97,7 @@ func (d *evaluationData) GetForEachAttr(addr addrs.ForEachAttr, rng hcl.Range) (
14297func (d * evaluationData ) GetInputVariable (addr addrs.InputVariable , rng hcl.Range ) (cty.Value , hcl.Diagnostics ) {
14398 var diags hcl.Diagnostics
14499
145- moduleConfig := d .Evaluator . Config .DescendentForInstance (d .ModulePath )
100+ moduleConfig := d .Config .DescendentForInstance (d .ModulePath )
146101 if moduleConfig == nil {
147102 // should never happen, since we can't be evaluating in a module
148103 // that wasn't mentioned in configuration.
@@ -172,7 +127,7 @@ func (d *evaluationData) GetInputVariable(addr addrs.InputVariable, rng hcl.Rang
172127 }
173128
174129 moduleAddrStr := d .ModulePath .String ()
175- vals := d .Evaluator . VariableValues [moduleAddrStr ]
130+ vals := d .VariableValues [moduleAddrStr ]
176131 if vals == nil {
177132 return cty .UnknownVal (config .Type ), diags
178133 }
@@ -229,7 +184,7 @@ func (d *evaluationData) GetLocalValue(addr addrs.LocalValue, rng hcl.Range) (ct
229184
230185 // First we'll make sure the requested value is declared in configuration,
231186 // so we can produce a nice message if not.
232- moduleConfig := d .Evaluator . Config .DescendentForInstance (d .ModulePath )
187+ moduleConfig := d .Config .DescendentForInstance (d .ModulePath )
233188 if moduleConfig == nil {
234189 // should never happen, since we can't be evaluating in a module
235190 // that wasn't mentioned in configuration.
@@ -257,13 +212,13 @@ func (d *evaluationData) GetLocalValue(addr addrs.LocalValue, rng hcl.Range) (ct
257212 }
258213
259214 // Build a call stack for circular reference detection only when getting a local value.
260- if diags := d .Evaluator .CallStack .Push (addrs.Reference {Subject : addr , SourceRange : rng }); diags .HasErrors () {
215+ if diags := d .Scope .CallStack .Push (addrs.Reference {Subject : addr , SourceRange : rng }); diags .HasErrors () {
261216 return cty .UnknownVal (cty .DynamicPseudoType ), diags
262217 }
263218
264- val , diags := d .Evaluator . EvaluateExpr (config .Expr , cty .DynamicPseudoType )
219+ val , diags := d .Scope . EvalExpr (config .Expr , cty .DynamicPseudoType )
265220
266- d .Evaluator .CallStack .Pop ()
221+ d .Scope .CallStack .Pop ()
267222 return val , diags
268223}
269224
@@ -274,10 +229,10 @@ func (d *evaluationData) GetPathAttr(addr addrs.PathAttr, rng hcl.Range) (cty.Va
274229 case "cwd" :
275230 var err error
276231 var wd string
277- if d .Evaluator . Meta != nil {
232+ if d .Meta != nil {
278233 // Meta is always non-nil in the normal case, but some test cases
279234 // are not so realistic.
280- wd = d .Evaluator . Meta .OriginalWorkingDir
235+ wd = d .Meta .OriginalWorkingDir
281236 }
282237 if wd == "" {
283238 wd , err = os .Getwd ()
@@ -308,7 +263,7 @@ func (d *evaluationData) GetPathAttr(addr addrs.PathAttr, rng hcl.Range) (cty.Va
308263 return cty .StringVal (filepath .ToSlash (wd )), diags
309264
310265 case "module" :
311- moduleConfig := d .Evaluator . Config .DescendentForInstance (d .ModulePath )
266+ moduleConfig := d .Config .DescendentForInstance (d .ModulePath )
312267 if moduleConfig == nil {
313268 // should never happen, since we can't be evaluating in a module
314269 // that wasn't mentioned in configuration.
@@ -318,7 +273,7 @@ func (d *evaluationData) GetPathAttr(addr addrs.PathAttr, rng hcl.Range) (cty.Va
318273 return cty .StringVal (filepath .ToSlash (sourceDir )), diags
319274
320275 case "root" :
321- sourceDir := d .Evaluator . Config .Module .SourceDir
276+ sourceDir := d .Config .Module .SourceDir
322277 return cty .StringVal (filepath .ToSlash (sourceDir )), diags
323278
324279 default :
@@ -341,7 +296,7 @@ func (d *evaluationData) GetTerraformAttr(addr addrs.TerraformAttr, rng hcl.Rang
341296 switch addr .Name {
342297
343298 case "workspace" :
344- workspaceName := d .Evaluator . Meta .Env
299+ workspaceName := d .Meta .Env
345300 return cty .StringVal (workspaceName ), diags
346301
347302 case "env" :
0 commit comments