Ability to look up the variables in an expression
This commit is contained in:
parent
73b5ba8089
commit
1535d4b708
@ -571,3 +571,7 @@ func (e *fixedExpression) Range() (r zcl.Range) {
|
||||
func (e *fixedExpression) StartRange() (r zcl.Range) {
|
||||
return
|
||||
}
|
||||
|
||||
func (e *fixedExpression) Variables() []zcl.Traversal {
|
||||
return nil
|
||||
}
|
||||
|
@ -310,6 +310,34 @@ func (e *expression) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
||||
return ctyValueFromHCLNode(e.src, ctx)
|
||||
}
|
||||
|
||||
func (e *expression) Variables() []zcl.Traversal {
|
||||
node := e.src
|
||||
var vars []zcl.Traversal
|
||||
|
||||
switch tn := node.(type) {
|
||||
case *hclast.LiteralType:
|
||||
tok := tn.Token
|
||||
switch tok.Type {
|
||||
case hcltoken.STRING, hcltoken.HEREDOC:
|
||||
// TODO: HIL parsing and evaluation, if ctx is non-nil.
|
||||
}
|
||||
case *hclast.ObjectType:
|
||||
list := tn.List
|
||||
attrs, _ := (&body{oli: list}).JustAttributes()
|
||||
if attrs != nil {
|
||||
for _, attr := range attrs {
|
||||
vars = append(vars, attr.Expr.Variables()...)
|
||||
}
|
||||
}
|
||||
case *hclast.ListType:
|
||||
nodes := tn.List
|
||||
for _, node := range nodes {
|
||||
vars = append(vars, (&expression{src: node}).Variables()...)
|
||||
}
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
func (e *expression) Range() zcl.Range {
|
||||
return rangeFromHCLPos(e.src.Pos())
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package hclhil
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/apparentlymart/go-cty/cty"
|
||||
"github.com/apparentlymart/go-cty/cty/function"
|
||||
@ -45,6 +46,58 @@ func (e *templateExpression) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnos
|
||||
return ctyValueFromHILNode(e.node, cfg)
|
||||
}
|
||||
|
||||
func (e *templateExpression) Variables() []zcl.Traversal {
|
||||
var vars []zcl.Traversal
|
||||
e.node.Accept(func(n hilast.Node) hilast.Node {
|
||||
vn, ok := n.(*hilast.VariableAccess)
|
||||
if !ok {
|
||||
return n
|
||||
}
|
||||
|
||||
rawName := vn.Name
|
||||
parts := strings.Split(rawName, ".")
|
||||
if len(parts) == 0 {
|
||||
return n
|
||||
}
|
||||
|
||||
tr := make(zcl.Traversal, 0, len(parts))
|
||||
tr = append(tr, zcl.TraverseRoot{
|
||||
Name: parts[0],
|
||||
SrcRange: rangeFromHILPos(n.Pos()),
|
||||
})
|
||||
|
||||
for _, name := range parts {
|
||||
if nv, err := strconv.Atoi(name); err == nil {
|
||||
// Turn this into a sequence index in zcl land, to save
|
||||
// callers from having to understand both HIL-style numeric
|
||||
// attributes and zcl-style indices.
|
||||
tr = append(tr, zcl.TraverseIndex{
|
||||
Key: cty.NumberIntVal(int64(nv)),
|
||||
SrcRange: rangeFromHILPos(n.Pos()),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if name == "*" {
|
||||
// TODO: support splat traversals, but that requires some
|
||||
// more work here because we need to then accumulate the
|
||||
// rest of the parts into the splat's own "Each" traversal.
|
||||
continue
|
||||
}
|
||||
|
||||
tr = append(tr, zcl.TraverseAttr{
|
||||
Name: name,
|
||||
SrcRange: rangeFromHILPos(n.Pos()),
|
||||
})
|
||||
}
|
||||
|
||||
vars = append(vars, tr)
|
||||
|
||||
return n
|
||||
})
|
||||
return vars
|
||||
}
|
||||
|
||||
func (e *templateExpression) Range() zcl.Range {
|
||||
return rangeFromHILPos(e.node.Pos())
|
||||
}
|
||||
|
@ -307,6 +307,45 @@ func (e *expression) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *expression) Variables() []zcl.Traversal {
|
||||
var vars []zcl.Traversal
|
||||
|
||||
switch v := e.src.(type) {
|
||||
case *stringVal:
|
||||
if e.useHIL {
|
||||
// Legacy interface to parse HCL-style JSON with HIL expressions.
|
||||
templateSrc := v.Value
|
||||
hilExpr, _ := hclhil.ParseTemplateEmbedded(
|
||||
[]byte(templateSrc),
|
||||
v.SrcRange.Filename,
|
||||
zcl.Pos{
|
||||
// skip over the opening quote mark
|
||||
Byte: v.SrcRange.Start.Byte + 1,
|
||||
Line: v.SrcRange.Start.Line,
|
||||
Column: v.SrcRange.Start.Column,
|
||||
},
|
||||
)
|
||||
if hilExpr != nil {
|
||||
vars = append(vars, hilExpr.Variables()...)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Once the native zcl template language parser is implemented,
|
||||
// parse with that and look for variables in there too,
|
||||
|
||||
case *arrayVal:
|
||||
for _, jsonVal := range v.Values {
|
||||
vars = append(vars, (&expression{src: jsonVal, useHIL: e.useHIL}).Variables()...)
|
||||
}
|
||||
case *objectVal:
|
||||
for _, jsonAttr := range v.Attrs {
|
||||
vars = append(vars, (&expression{src: jsonAttr.Value, useHIL: e.useHIL}).Variables()...)
|
||||
}
|
||||
}
|
||||
|
||||
return vars
|
||||
}
|
||||
|
||||
func (e *expression) Range() zcl.Range {
|
||||
return e.src.Range()
|
||||
}
|
||||
|
@ -110,12 +110,15 @@ type Expression interface {
|
||||
// the specific symbol in question.
|
||||
Value(ctx *EvalContext) (cty.Value, Diagnostics)
|
||||
|
||||
// Variables returns a list of variables referenced in the receiving
|
||||
// expression. These are expressed as absolute Traversals, so may include
|
||||
// additional information about how the variable is used, such as
|
||||
// attribute lookups, which the calling application can potentially use
|
||||
// to only selectively populate the scope.
|
||||
Variables() []Traversal
|
||||
|
||||
Range() Range
|
||||
StartRange() Range
|
||||
|
||||
// TODO: A "Variables" method that returns a description of all of the
|
||||
// variables used in the expression, so callers can populate the scope
|
||||
// only with variables that are actually used.
|
||||
}
|
||||
|
||||
// OfType filters the receiving block sequence by block type name,
|
||||
|
Loading…
Reference in New Issue
Block a user