@ -49,29 +49,29 @@ type parser struct {
attrs map [ string ] * hcl . Attribute
funcs map [ string ] * functionDef
blocks map [ string ] map [ string ] [ ] * hcl . Block
blockValues map [ * hcl . Block ] reflect . Value
blockTypes map [ string ] reflect . Type
blocks map [ string ] map [ string ] [ ] * hcl . Block
blockValues map [ * hcl . Block ] reflect . Value
blockEvalCtx map [ * hcl . Block ] * hcl . EvalContext
blockTypes map [ string ] reflect . Type
ectx * hcl . EvalContext
progress map [ string ] struct { }
progressF map [ string ] struct { }
progressB map [ * hcl . Block ] map [ string ] struct { }
doneF map [ string ] struct { }
doneB map [ * hcl . Block ] map [ string ] struct { }
}
var errUndefined = errors . New ( "undefined" )
func ( p * parser ) loadDeps ( e xp hcl . Expression , exclude map [ string ] struct { } , allowMissing bool ) hcl . Diagnostics {
func ( p * parser ) loadDeps ( e ctx * hcl . EvalContext , e xp hcl . Expression , exclude map [ string ] struct { } , allowMissing bool ) hcl . Diagnostics {
fns , hcldiags := funcCalls ( exp )
if hcldiags . HasErrors ( ) {
return hcldiags
}
for _ , fn := range fns {
if err := p . resolveFunction ( fn) ; err != nil {
if err := p . resolveFunction ( ectx, fn) ; err != nil {
if allowMissing && errors . Is ( err , errUndefined ) {
continue
}
@ -131,7 +131,7 @@ func (p *parser) loadDeps(exp hcl.Expression, exclude map[string]struct{}, allow
return wrapErrorDiagnostic ( "Invalid expression" , err , exp . Range ( ) . Ptr ( ) , exp . Range ( ) . Ptr ( ) )
}
} else {
if err := p . resolveValue ( v. RootName ( ) ) ; err != nil {
if err := p . resolveValue ( ectx, v. RootName ( ) ) ; err != nil {
if allowMissing && errors . Is ( err , errUndefined ) {
continue
}
@ -145,16 +145,16 @@ func (p *parser) loadDeps(exp hcl.Expression, exclude map[string]struct{}, allow
// resolveFunction forces evaluation of a function, storing the result into the
// parser.
func ( p * parser ) resolveFunction ( name string ) error {
if _ , ok := p . doneF [ name ] ; ok {
func ( p * parser ) resolveFunction ( ectx * hcl . EvalContext , name string ) error {
if _ , ok := p . ectx . Functions [ name ] ; ok {
return nil
}
if _ , ok := ectx . Functions [ name ] ; ok {
return nil
}
f , ok := p . funcs [ name ]
if ! ok {
if _ , ok := p . ectx . Functions [ name ] ; ok {
return nil
}
return errors . Wrapf ( errUndefined , "function %q does not exit" , name )
return errors . Wrapf ( errUndefined , "function %q does not exist" , name )
}
if _ , ok := p . progressF [ name ] ; ok {
return errors . Errorf ( "function cycle not allowed for %s" , name )
@ -204,7 +204,7 @@ func (p *parser) resolveFunction(name string) error {
return diags
}
if diags := p . loadDeps ( f. Result . Expr , params , false ) ; diags . HasErrors ( ) {
if diags := p . loadDeps ( p. ectx , f. Result . Expr , params , false ) ; diags . HasErrors ( ) {
return diags
}
@ -214,7 +214,6 @@ func (p *parser) resolveFunction(name string) error {
if diags . HasErrors ( ) {
return diags
}
p . doneF [ name ] = struct { } { }
p . ectx . Functions [ name ] = v
return nil
@ -222,10 +221,13 @@ func (p *parser) resolveFunction(name string) error {
// resolveValue forces evaluation of a named value, storing the result into the
// parser.
func ( p * parser ) resolveValue ( name string ) ( err error ) {
func ( p * parser ) resolveValue ( ectx * hcl . EvalContext , name string ) ( err error ) {
if _ , ok := p . ectx . Variables [ name ] ; ok {
return nil
}
if _ , ok := ectx . Variables [ name ] ; ok {
return nil
}
if _ , ok := p . progress [ name ] ; ok {
return errors . Errorf ( "variable cycle not allowed for %s" , name )
}
@ -242,9 +244,10 @@ func (p *parser) resolveValue(name string) (err error) {
if _ , builtin := p . opt . Vars [ name ] ; ! ok && ! builtin {
vr , ok := p . vars [ name ]
if ! ok {
return errors . Wrapf ( errUndefined , "variable %q does not exi t", name )
return errors . Wrapf ( errUndefined , "variable %q does not exi s t", name )
}
def = vr . Default
ectx = p . ectx
}
if def == nil {
@ -257,10 +260,10 @@ func (p *parser) resolveValue(name string) (err error) {
return
}
if diags := p . loadDeps ( def. Expr , nil , true ) ; diags . HasErrors ( ) {
if diags := p . loadDeps ( ectx, def. Expr , nil , true ) ; diags . HasErrors ( ) {
return diags
}
vv , diags := def . Expr . Value ( p. ectx)
vv , diags := def . Expr . Value ( ectx)
if diags . HasErrors ( ) {
return diags
}
@ -364,18 +367,43 @@ func (p *parser) resolveBlock(block *hcl.Block, target *hcl.BodySchema) (err err
return FilterExcludeBody ( block . Body , filter )
}
// load dependencies from all targeted properties
// prepare the output destination and evaluation context
t , ok := p . blockTypes [ block . Type ]
if ! ok {
return nil
}
var output reflect . Value
var ectx * hcl . EvalContext
if prev , ok := p . blockValues [ block ] ; ok {
output = prev
ectx = p . blockEvalCtx [ block ]
} else {
output = reflect . New ( t )
setLabel ( output , block . Labels [ 0 ] ) // early attach labels, so we can reference them
type ectxI interface {
EvalContext ( base * hcl . EvalContext , block * hcl . Block ) * hcl . EvalContext
}
if v , ok := output . Interface ( ) . ( ectxI ) ; ok {
ectx = v . EvalContext ( p . ectx , block )
if ectx != p . ectx && ectx . Parent ( ) != p . ectx {
return errors . Errorf ( "EvalContext must return a context with the correct parent" )
}
} else {
ectx = p . ectx
}
}
p . blockValues [ block ] = output
p . blockEvalCtx [ block ] = ectx
// load dependencies from all targeted properties
schema , _ := gohcl . ImpliedBodySchema ( reflect . New ( t ) . Interface ( ) )
content , _ , diag := body ( ) . PartialContent ( schema )
if diag . HasErrors ( ) {
return diag
}
for _ , a := range content . Attributes {
diag := p . loadDeps ( a . Expr , nil , true )
diag := p . loadDeps ( ectx, a. Expr , nil , true )
if diag . HasErrors ( ) {
return diag
}
@ -388,18 +416,10 @@ func (p *parser) resolveBlock(block *hcl.Block, target *hcl.BodySchema) (err err
}
// decode!
var output reflect . Value
if prev , ok := p . blockValues [ block ] ; ok {
output = prev
} else {
output = reflect . New ( t )
setLabel ( output , block . Labels [ 0 ] ) // early attach labels, so we can reference them
}
diag = gohcl . DecodeBody ( body ( ) , p . ectx , output . Interface ( ) )
diag = gohcl . DecodeBody ( body ( ) , ectx , output . Interface ( ) )
if diag . HasErrors ( ) {
return diag
}
p . blockValues [ block ] = output
// mark all targeted properties as done
for _ , a := range content . Attributes {
@ -417,7 +437,7 @@ func (p *parser) resolveBlock(block *hcl.Block, target *hcl.BodySchema) (err err
}
}
// store the result into the evaluation context (so i f can be referenced)
// store the result into the evaluation context (so i t can be referenced)
outputType , err := gocty . ImpliedType ( output . Interface ( ) )
if err != nil {
return err
@ -475,20 +495,20 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
attrs : map [ string ] * hcl . Attribute { } ,
funcs : map [ string ] * functionDef { } ,
blocks : map [ string ] map [ string ] [ ] * hcl . Block { } ,
blockValues : map [ * hcl . Block ] reflect . Value { } ,
blockTypes : map [ string ] reflect . Type { } ,
progress : map [ string ] struct { } { } ,
progressF : map [ string ] struct { } { } ,
progressB : map [ * hcl . Block ] map [ string ] struct { } { } ,
blocks : map [ string ] map [ string ] [ ] * hcl . Block { } ,
blockValues : map [ * hcl . Block ] reflect . Value { } ,
blockEvalCtx : map [ * hcl . Block ] * hcl . EvalContext { } ,
blockTypes : map [ string ] reflect . Type { } ,
doneF : map [ string ] struct { } { } ,
doneB : map [ * hcl . Block ] map [ string ] struct { } { } ,
ectx : & hcl . EvalContext {
Variables : map [ string ] cty . Value { } ,
Functions : stdlibFunctions ,
Functions : Stdlib ( ) ,
} ,
progress : map [ string ] struct { } { } ,
progressF : map [ string ] struct { } { } ,
progressB : map [ * hcl . Block ] map [ string ] struct { } { } ,
doneB : map [ * hcl . Block ] map [ string ] struct { } { } ,
}
for _ , v := range defs . Variables {
@ -532,7 +552,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
delete ( p . attrs , "function" )
for k := range p . opt . Vars {
_ = p . resolveValue ( k)
_ = p . resolveValue ( p. ectx , k)
}
for _ , a := range content . Attributes {
@ -548,7 +568,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
}
for k := range p . vars {
if err := p . resolveValue ( k) ; err != nil {
if err := p . resolveValue ( p. ectx , k) ; err != nil {
if diags , ok := err . ( hcl . Diagnostics ) ; ok {
return diags
}
@ -558,7 +578,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
}
for k := range p . funcs {
if err := p . resolveFunction ( k) ; err != nil {
if err := p . resolveFunction ( p. ectx , k) ; err != nil {
if diags , ok := err . ( hcl . Diagnostics ) ; ok {
return diags
}
@ -678,7 +698,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
}
for k := range p . attrs {
if err := p . resolveValue ( k) ; err != nil {
if err := p . resolveValue ( p. ectx , k) ; err != nil {
if diags , ok := err . ( hcl . Diagnostics ) ; ok {
return diags
}