Ability to look up the variables in an expression

This commit is contained in:
Martin Atkins 2017-05-22 18:54:23 -07:00
parent 73b5ba8089
commit 1535d4b708
5 changed files with 131 additions and 4 deletions

View File

@ -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
}

View File

@ -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())
}

View File

@ -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())
}

View File

@ -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()
}

View File

@ -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,