3 // AbsTraversalForExpr attempts to interpret the given expression as
4 // an absolute traversal, or returns error diagnostic(s) if that is
5 // not possible for the given expression.
7 // A particular Expression implementation can support this function by
8 // offering a method called AsTraversal that takes no arguments and
9 // returns either a valid absolute traversal or nil to indicate that
10 // no traversal is possible. Alternatively, an implementation can support
11 // UnwrapExpression to delegate handling of this function to a wrapped
14 // In most cases the calling application is interested in the value
15 // that results from an expression, but in rarer cases the application
16 // needs to see the the name of the variable and subsequent
17 // attributes/indexes itself, for example to allow users to give references
18 // to the variables themselves rather than to their values. An implementer
19 // of this function should at least support attribute and index steps.
20 func AbsTraversalForExpr(expr Expression) (Traversal, Diagnostics) {
21 type asTraversal interface {
22 AsTraversal() Traversal
25 physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool {
26 _, supported := expr.(asTraversal)
30 if asT, supported := physExpr.(asTraversal); supported {
31 if traversal := asT.AsTraversal(); traversal != nil {
35 return nil, Diagnostics{
38 Summary: "Invalid expression",
39 Detail: "A static variable reference is required.",
40 Subject: expr.Range().Ptr(),
45 // RelTraversalForExpr is similar to AbsTraversalForExpr but it returns
46 // a relative traversal instead. Due to the nature of HCL expressions, the
47 // first element of the returned traversal is always a TraverseAttr, and
48 // then it will be followed by zero or more other expressions.
50 // Any expression accepted by AbsTraversalForExpr is also accepted by
51 // RelTraversalForExpr.
52 func RelTraversalForExpr(expr Expression) (Traversal, Diagnostics) {
53 traversal, diags := AbsTraversalForExpr(expr)
54 if len(traversal) > 0 {
55 ret := make(Traversal, len(traversal))
57 root := traversal[0].(TraverseRoot)
58 ret[0] = TraverseAttr{
60 SrcRange: root.SrcRange,
64 return traversal, diags
67 // ExprAsKeyword attempts to interpret the given expression as a static keyword,
68 // returning the keyword string if possible, and the empty string if not.
70 // A static keyword, for the sake of this function, is a single identifier.
71 // For example, the following attribute has an expression that would produce
76 // This function is a variant of AbsTraversalForExpr, which uses the same
77 // interface on the given expression. This helper constrains the result
78 // further by requiring only a single root identifier.
80 // This function is intended to be used with the following idiom, to recognize
81 // situations where one of a fixed set of keywords is required and arbitrary
82 // expressions are not allowed:
84 // switch hcl.ExprAsKeyword(expr) {
86 // // (take suitable action for keyword "allow")
88 // // (take suitable action for keyword "deny")
90 // diags = append(diags, &hcl.Diagnostic{
91 // // ... "invalid keyword" diagnostic message ...
95 // The above approach will generate the same message for both the use of an
96 // unrecognized keyword and for not using a keyword at all, which is usually
97 // reasonable if the message specifies that the given value must be a keyword
98 // from that fixed list.
100 // Note that in the native syntax the keywords "true", "false", and "null" are
101 // recognized as literal values during parsing and so these reserved words
102 // cannot not be accepted as keywords by this function.
104 // Since interpreting an expression as a keyword bypasses usual expression
105 // evaluation, it should be used sparingly for situations where e.g. one of
106 // a fixed set of keywords is used in a structural way in a special attribute
107 // to affect the further processing of a block.
108 func ExprAsKeyword(expr Expression) string {
109 type asTraversal interface {
110 AsTraversal() Traversal
113 physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool {
114 _, supported := expr.(asTraversal)
118 if asT, supported := physExpr.(asTraversal); supported {
119 if traversal := asT.AsTraversal(); len(traversal) == 1 {
120 return traversal.RootName()