]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package parser |
2 | ||
3 | import "github.com/hashicorp/hcl/hcl/ast" | |
4 | ||
5 | // flattenObjects takes an AST node, walks it, and flattens | |
6 | func flattenObjects(node ast.Node) { | |
7 | ast.Walk(node, func(n ast.Node) (ast.Node, bool) { | |
8 | // We only care about lists, because this is what we modify | |
9 | list, ok := n.(*ast.ObjectList) | |
10 | if !ok { | |
11 | return n, true | |
12 | } | |
13 | ||
14 | // Rebuild the item list | |
15 | items := make([]*ast.ObjectItem, 0, len(list.Items)) | |
16 | frontier := make([]*ast.ObjectItem, len(list.Items)) | |
17 | copy(frontier, list.Items) | |
18 | for len(frontier) > 0 { | |
19 | // Pop the current item | |
20 | n := len(frontier) | |
21 | item := frontier[n-1] | |
22 | frontier = frontier[:n-1] | |
23 | ||
24 | switch v := item.Val.(type) { | |
25 | case *ast.ObjectType: | |
26 | items, frontier = flattenObjectType(v, item, items, frontier) | |
27 | case *ast.ListType: | |
28 | items, frontier = flattenListType(v, item, items, frontier) | |
29 | default: | |
30 | items = append(items, item) | |
31 | } | |
32 | } | |
33 | ||
34 | // Reverse the list since the frontier model runs things backwards | |
35 | for i := len(items)/2 - 1; i >= 0; i-- { | |
36 | opp := len(items) - 1 - i | |
37 | items[i], items[opp] = items[opp], items[i] | |
38 | } | |
39 | ||
40 | // Done! Set the original items | |
41 | list.Items = items | |
42 | return n, true | |
43 | }) | |
44 | } | |
45 | ||
46 | func flattenListType( | |
47 | ot *ast.ListType, | |
48 | item *ast.ObjectItem, | |
49 | items []*ast.ObjectItem, | |
50 | frontier []*ast.ObjectItem) ([]*ast.ObjectItem, []*ast.ObjectItem) { | |
51 | // If the list is empty, keep the original list | |
52 | if len(ot.List) == 0 { | |
53 | items = append(items, item) | |
54 | return items, frontier | |
55 | } | |
56 | ||
57 | // All the elements of this object must also be objects! | |
58 | for _, subitem := range ot.List { | |
59 | if _, ok := subitem.(*ast.ObjectType); !ok { | |
60 | items = append(items, item) | |
61 | return items, frontier | |
62 | } | |
63 | } | |
64 | ||
65 | // Great! We have a match go through all the items and flatten | |
66 | for _, elem := range ot.List { | |
67 | // Add it to the frontier so that we can recurse | |
68 | frontier = append(frontier, &ast.ObjectItem{ | |
69 | Keys: item.Keys, | |
70 | Assign: item.Assign, | |
71 | Val: elem, | |
72 | LeadComment: item.LeadComment, | |
73 | LineComment: item.LineComment, | |
74 | }) | |
75 | } | |
76 | ||
77 | return items, frontier | |
78 | } | |
79 | ||
80 | func flattenObjectType( | |
81 | ot *ast.ObjectType, | |
82 | item *ast.ObjectItem, | |
83 | items []*ast.ObjectItem, | |
84 | frontier []*ast.ObjectItem) ([]*ast.ObjectItem, []*ast.ObjectItem) { | |
85 | // If the list has no items we do not have to flatten anything | |
86 | if ot.List.Items == nil { | |
87 | items = append(items, item) | |
88 | return items, frontier | |
89 | } | |
90 | ||
91 | // All the elements of this object must also be objects! | |
92 | for _, subitem := range ot.List.Items { | |
93 | if _, ok := subitem.Val.(*ast.ObjectType); !ok { | |
94 | items = append(items, item) | |
95 | return items, frontier | |
96 | } | |
97 | } | |
98 | ||
99 | // Great! We have a match go through all the items and flatten | |
100 | for _, subitem := range ot.List.Items { | |
101 | // Copy the new key | |
102 | keys := make([]*ast.ObjectKey, len(item.Keys)+len(subitem.Keys)) | |
103 | copy(keys, item.Keys) | |
104 | copy(keys[len(item.Keys):], subitem.Keys) | |
105 | ||
106 | // Add it to the frontier so that we can recurse | |
107 | frontier = append(frontier, &ast.ObjectItem{ | |
108 | Keys: keys, | |
109 | Assign: item.Assign, | |
110 | Val: subitem.Val, | |
111 | LeadComment: item.LeadComment, | |
112 | LineComment: item.LineComment, | |
113 | }) | |
114 | } | |
115 | ||
116 | return items, frontier | |
117 | } |