diff --git a/hcl/expr_list.go b/hcl/expr_list.go new file mode 100644 index 0000000..b06b197 --- /dev/null +++ b/hcl/expr_list.go @@ -0,0 +1,30 @@ +package hcl + +// ExprList tests if the given expression is a static list construct and, +// if so, extracts the expressions that represent the list elements. +// If the given expression is not a static list, error diagnostics are +// returned. +// +// A particular Expression implementation can support this function by +// offering a method called ExprList that takes no arguments and returns +// []Expression. This method should return nil if a static list cannot +// be extracted. +func ExprList(expr Expression) ([]Expression, Diagnostics) { + type exprList interface { + ExprList() []Expression + } + + if exL, supported := expr.(exprList); supported { + if list := exL.ExprList(); list != nil { + return list, nil + } + } + return nil, Diagnostics{ + &Diagnostic{ + Severity: DiagError, + Summary: "Invalid expression", + Detail: "A static list expression is required.", + Subject: expr.StartRange().Ptr(), + }, + } +} diff --git a/hcl/hclsyntax/expression.go b/hcl/hclsyntax/expression.go index 44e837b..58b6b15 100644 --- a/hcl/hclsyntax/expression.go +++ b/hcl/hclsyntax/expression.go @@ -544,6 +544,15 @@ func (e *TupleConsExpr) StartRange() hcl.Range { return e.OpenRange } +// Implementation for hcl.ExprList +func (e *TupleConsExpr) ExprList() []hcl.Expression { + ret := make([]hcl.Expression, len(e.Exprs)) + for i, expr := range e.Exprs { + ret[i] = expr + } + return ret +} + type ObjectConsExpr struct { Items []ObjectConsItem diff --git a/hcl/hclsyntax/expression_test.go b/hcl/hclsyntax/expression_test.go index c4eb772..1b0030d 100644 --- a/hcl/hclsyntax/expression_test.go +++ b/hcl/hclsyntax/expression_test.go @@ -1101,3 +1101,21 @@ func TestExpressionAsTraversal(t *testing.T) { t.Fatalf("wrong root name %q; want %q", traversal.RootName(), "a") } } + +func TestStaticExpressionList(t *testing.T) { + expr, _ := ParseExpression([]byte("[0, a, true]"), "", hcl.Pos{}) + exprs, diags := hcl.ExprList(expr) + if len(diags) != 0 { + t.Fatalf("unexpected diagnostics") + } + if len(exprs) != 3 { + t.Fatalf("wrong result %#v; want length 3", exprs) + } + first, ok := exprs[0].(*LiteralValueExpr) + if !ok { + t.Fatalf("first expr has wrong type %T; want *zclsyntax.LiteralValueExpr", exprs[0]) + } + if !first.Val.RawEquals(cty.Zero) { + t.Fatalf("wrong first value %#v; want cty.Zero", first.Val) + } +} diff --git a/hcl/json/structure.go b/hcl/json/structure.go index dae8183..27149f6 100644 --- a/hcl/json/structure.go +++ b/hcl/json/structure.go @@ -370,3 +370,17 @@ func (e *expression) AsTraversal() hcl.Traversal { return nil } } + +// Implementation for hcl.ExprList. +func (e *expression) ExprList() []hcl.Expression { + switch v := e.src.(type) { + case *arrayVal: + ret := make([]hcl.Expression, len(v.Values)) + for i, node := range v.Values { + ret[i] = &expression{src: node} + } + return ret + default: + return nil + } +} diff --git a/hcl/json/structure_test.go b/hcl/json/structure_test.go index 0bf806a..7600e20 100644 --- a/hcl/json/structure_test.go +++ b/hcl/json/structure_test.go @@ -753,3 +753,22 @@ func TestExpressionAsTraversal(t *testing.T) { t.Fatalf("incorrect traversal %#v; want length 3", traversal) } } + +func TestStaticExpressionList(t *testing.T) { + e := &expression{ + src: &arrayVal{ + Values: []node{ + &stringVal{ + Value: "hello", + }, + }, + }, + } + exprs := e.ExprList() + if len(exprs) != 1 { + t.Fatalf("incorrect exprs %#v; want length 1", exprs) + } + if exprs[0].(*expression).src != e.src.(*arrayVal).Values[0] { + t.Fatalf("wrong first expression node") + } +}