| package hcl |
| |
| // AbsTraversalForExpr attempts to interpret the given expression as |
| // an absolute traversal, or returns error diagnostic(s) if that is |
| // not possible for the given expression. |
| // |
| // A particular Expression implementation can support this function by |
| // offering a method called AsTraversal that takes no arguments and |
| // returns either a valid absolute traversal or nil to indicate that |
| // no traversal is possible. Alternatively, an implementation can support |
| // UnwrapExpression to delegate handling of this function to a wrapped |
| // Expression object. |
| // |
| // In most cases the calling application is interested in the value |
| // that results from an expression, but in rarer cases the application |
| // needs to see the the name of the variable and subsequent |
| // attributes/indexes itself, for example to allow users to give references |
| // to the variables themselves rather than to their values. An implementer |
| // of this function should at least support attribute and index steps. |
| func AbsTraversalForExpr(expr Expression) (Traversal, Diagnostics) { |
| type asTraversal interface { |
| AsTraversal() Traversal |
| } |
| |
| physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool { |
| _, supported := expr.(asTraversal) |
| return supported |
| }) |
| |
| if asT, supported := physExpr.(asTraversal); supported { |
| if traversal := asT.AsTraversal(); traversal != nil { |
| return traversal, nil |
| } |
| } |
| return nil, Diagnostics{ |
| &Diagnostic{ |
| Severity: DiagError, |
| Summary: "Invalid expression", |
| Detail: "A single static variable reference is required: only attribute access and indexing with constant keys. No calculations, function calls, template expressions, etc are allowed here.", |
| Subject: expr.Range().Ptr(), |
| }, |
| } |
| } |
| |
| // RelTraversalForExpr is similar to AbsTraversalForExpr but it returns |
| // a relative traversal instead. Due to the nature of HCL expressions, the |
| // first element of the returned traversal is always a TraverseAttr, and |
| // then it will be followed by zero or more other expressions. |
| // |
| // Any expression accepted by AbsTraversalForExpr is also accepted by |
| // RelTraversalForExpr. |
| func RelTraversalForExpr(expr Expression) (Traversal, Diagnostics) { |
| traversal, diags := AbsTraversalForExpr(expr) |
| if len(traversal) > 0 { |
| ret := make(Traversal, len(traversal)) |
| copy(ret, traversal) |
| root := traversal[0].(TraverseRoot) |
| ret[0] = TraverseAttr{ |
| Name: root.Name, |
| SrcRange: root.SrcRange, |
| } |
| return ret, diags |
| } |
| return traversal, diags |
| } |
| |
| // ExprAsKeyword attempts to interpret the given expression as a static keyword, |
| // returning the keyword string if possible, and the empty string if not. |
| // |
| // A static keyword, for the sake of this function, is a single identifier. |
| // For example, the following attribute has an expression that would produce |
| // the keyword "foo": |
| // |
| // example = foo |
| // |
| // This function is a variant of AbsTraversalForExpr, which uses the same |
| // interface on the given expression. This helper constrains the result |
| // further by requiring only a single root identifier. |
| // |
| // This function is intended to be used with the following idiom, to recognize |
| // situations where one of a fixed set of keywords is required and arbitrary |
| // expressions are not allowed: |
| // |
| // switch hcl.ExprAsKeyword(expr) { |
| // case "allow": |
| // // (take suitable action for keyword "allow") |
| // case "deny": |
| // // (take suitable action for keyword "deny") |
| // default: |
| // diags = append(diags, &hcl.Diagnostic{ |
| // // ... "invalid keyword" diagnostic message ... |
| // }) |
| // } |
| // |
| // The above approach will generate the same message for both the use of an |
| // unrecognized keyword and for not using a keyword at all, which is usually |
| // reasonable if the message specifies that the given value must be a keyword |
| // from that fixed list. |
| // |
| // Note that in the native syntax the keywords "true", "false", and "null" are |
| // recognized as literal values during parsing and so these reserved words |
| // cannot not be accepted as keywords by this function. |
| // |
| // Since interpreting an expression as a keyword bypasses usual expression |
| // evaluation, it should be used sparingly for situations where e.g. one of |
| // a fixed set of keywords is used in a structural way in a special attribute |
| // to affect the further processing of a block. |
| func ExprAsKeyword(expr Expression) string { |
| type asTraversal interface { |
| AsTraversal() Traversal |
| } |
| |
| physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool { |
| _, supported := expr.(asTraversal) |
| return supported |
| }) |
| |
| if asT, supported := physExpr.(asTraversal); supported { |
| if traversal := asT.AsTraversal(); len(traversal) == 1 { |
| return traversal.RootName() |
| } |
| } |
| return "" |
| } |