Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion hclwrite/ast_expression.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright IBM Corp. 2014, 2025
// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: MPL-2.0

package hclwrite
Expand Down Expand Up @@ -187,6 +187,18 @@ Traversals:
}
}

func (e *Expression) AsObjectConsExpr() *ObjectConsExpr {
var found *ObjectConsExpr
e.walkChildNodes(func(n *node) {
if o, ok := n.content.(*ObjectConsExpr); ok {
found = o
return
}
})

return found
}

// Traversal represents a sequence of variable, attribute, and/or index
// operations.
type Traversal struct {
Expand Down
77 changes: 77 additions & 0 deletions hclwrite/ast_object_cons_expr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: MPL-2.0

package hclwrite

type ObjectConsExpr struct {
inTree
}

func newObjectConsExpr() *ObjectConsExpr {
return &ObjectConsExpr{
inTree: newInTree(),
}
}

func (o *ObjectConsExpr) ValueFor(key string) *ObjectConsValue {
var found *ObjectConsValue
o.walkChildNodes(func(n *node) {
if item, ok := n.content.(*ObjectConsItem); ok {
k := item.key.content.(*ObjectConsKey)
name := k.name.content.(*Expression)

// A temporary workaround for parsing expressions as names at ...
// parse-time
maybeKey := ""
for _, token := range name.BuildTokens(nil) {
maybeKey += string(token.Bytes) // no spaces before
}

if k.literal && (maybeKey == key || maybeKey == `"`+key+`"`) {
found = item.value.content.(*ObjectConsValue)
return
}
}
})
return found
}

type ObjectConsItem struct {
inTree
key *node
value *node
}

func newObjectConsItem() *ObjectConsItem {
return &ObjectConsItem{
inTree: newInTree(),
}
}

type ObjectConsKey struct {
inTree

literal bool
name *node
}

func newObjectConsKey() *ObjectConsKey {
return &ObjectConsKey{
inTree: newInTree(),
}
}

type ObjectConsValue struct {
inTree
expr *node
}

func newObjectConsValue() *ObjectConsValue {
return &ObjectConsValue{
inTree: newInTree(),
}
}

func (o *ObjectConsValue) Expr() *Expression {
return o.expr.content.(*Expression)
}
66 changes: 65 additions & 1 deletion hclwrite/parser.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright IBM Corp. 2014, 2025
// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: MPL-2.0

package hclwrite
Expand Down Expand Up @@ -376,6 +376,70 @@ func parseBlockLabels(nativeBlock *hclsyntax.Block, from inputTokens) (inputToke
}

func parseExpression(nativeExpr hclsyntax.Expression, from inputTokens) *node {
switch tNativeExpr := nativeExpr.(type) {

// Object-construct expression
case *hclsyntax.ObjectConsExpr:
return parseObjectConsExpr(tNativeExpr, from)

default:
return parseAnyExpression(nativeExpr, from)
}
}

// parseObjectConsExpr parses an object-construct expression, defined as:
//
// object = "{" (
// (objectelem (( "," | Newline) objectelem)* ","?)?
// ) "}";
// objectelem = (Identifier | Expression) ("=" | ":") Expression;
//
// It leaves any lead comments and line comments as unstructed tokens.
//
// It returns *Expression. The returned *Expression has a child *ObjectConsExpr
// that can be unwrapped via Expression.AsObjectConsExpr().
func parseObjectConsExpr(nativeExpr *hclsyntax.ObjectConsExpr, from inputTokens) *node {
expr := newObjectConsExpr()
children := expr.children

var before, open, keyTokens, valueTokens inputTokens

before, open, from = from.Partition(nativeExpr.StartRange())
children.AppendUnstructuredTokens(before.writerTokens)
children.AppendUnstructuredTokens(open.writerTokens)

for _, nativeItem := range nativeExpr.Items {
item := newObjectConsItem()
key, value := newObjectConsKey(), newObjectConsValue()

nativeKeyExpr := nativeItem.KeyExpr.(*hclsyntax.ObjectConsKeyExpr)
key.literal = !nativeKeyExpr.ForceNonLiteral

before, keyTokens, from = from.Partition(nativeKeyExpr.Range())
item.children.AppendUnstructuredTokens(before.writerTokens)
key.name = parseExpression(nativeKeyExpr, keyTokens)
key.children.AppendNode(key.name)
item.key = item.children.Append(key)

before, valueTokens, from = from.Partition(nativeItem.ValueExpr.Range())
item.children.AppendUnstructuredTokens(before.writerTokens)
value.expr = parseExpression(nativeItem.ValueExpr, valueTokens)
value.children.AppendNode(value.expr)
item.value = item.children.Append(value)

expr.children.Append(item)
}

_, from, _ = from.Partition(nativeExpr.Range())
children.AppendUnstructuredTokens(from.writerTokens)

// Wrap in an Expression
wrapExpr := newExpression()
wrapExpr.children.Append(expr)
return newNode(wrapExpr)
}

func parseAnyExpression(nativeExpr hclsyntax.Expression, from inputTokens) *node {
expr := newExpression()
children := expr.children

Expand Down
Loading