diff --git a/hcl2/reconstructor.py b/hcl2/reconstructor.py index 7f957d7b..ea5ce744 100644 --- a/hcl2/reconstructor.py +++ b/hcl2/reconstructor.py @@ -261,7 +261,6 @@ def _should_add_space(self, rule, current_terminal, is_block_label: bool = False if isinstance(self._last_rule, str) and re.match( r"^__(tuple|arguments)_(star|plus)_.*", self._last_rule ): - # string literals, decimals, and identifiers should always be # preceded by a space if they're following a comma in a tuple or # function arg @@ -362,6 +361,10 @@ def _name_to_identifier(name: str) -> Tree: """Converts a string to a NAME token within an identifier rule.""" return Tree(Token("RULE", "identifier"), [Token("NAME", name)]) + @staticmethod + def _is_valid_identifier(name: str) -> bool: + return re.match(r"^\w[\w\d]*$", name) is not None + @staticmethod def _escape_interpolated_str(interp_s: str) -> str: if interp_s.strip().startswith("<<-") or interp_s.strip().startswith("<<"): @@ -620,14 +623,14 @@ def _transform_value_to_expr_term(self, value, level) -> Union[Token, Tree]: continue value_expr_term = self._transform_value_to_expr_term(dict_v, level + 1) - k = self._unwrap_interpolation(k) + k = self._transform_value_to_key(k, level + 1) elements.append( Tree( Token("RULE", "object_elem"), [ Tree( Token("RULE", "object_elem_key"), - [Tree(Token("RULE", "identifier"), [Token("NAME", k)])], + [k], ), Token("EQ", " ="), value_expr_term, @@ -732,3 +735,20 @@ def _transform_value_to_expr_term(self, value, level) -> Union[Token, Tree]: # otherwise, we don't know the type raise RuntimeError(f"Unknown type to transform {type(value)}") + + def _transform_value_to_key(self, value, level) -> Tree: + """ + Convert any value to a suitable key for an object + """ + if self._is_valid_identifier(value): + return self._name_to_identifier(value) + expr = self._transform_value_to_expr_term(value, level) + # If the expression is a string, then return an object_elem_key of the string + if expr.data == "expr_term" and expr.children[0].data == "string": + return expr.children[0] + else: + # Otherwise return an expression + return Tree( + Token("RULE", "object_elem_key_expression"), + [Token("LPAR", "("), expr, Token("RPAR", ")")], + ) diff --git a/hcl2/transformer.py b/hcl2/transformer.py index 382092d6..6f5bd4fe 100644 --- a/hcl2/transformer.py +++ b/hcl2/transformer.py @@ -1,4 +1,5 @@ """A Lark Transformer for transforming a Lark parse tree into a Python dict""" + import json import re import sys @@ -103,9 +104,7 @@ def object_elem(self, args: List) -> Dict: # into a bigger dict that is returned by the "object" function key = str(args[0].children[0]) - if not re.match(r".*?(\${).*}.*", key): - # do not strip quotes of a interpolation string - key = self.strip_quotes(key) + key = self.strip_quotes(key) value = self.to_string_dollar(args[2]) return {key: value} @@ -114,7 +113,8 @@ def object_elem_key_dot_accessor(self, args: List) -> str: return "".join(args) def object_elem_key_expression(self, args: List) -> str: - return self.to_string_dollar("".join(args)) + # the first and third arguments are parentheses + return self.to_string_dollar(args[1]) def object(self, args: List) -> Dict: args = self.strip_new_line_tokens(args) diff --git a/test/helpers/terraform-config-json/variables.json b/test/helpers/terraform-config-json/variables.json index d344902c..2dba548e 100644 --- a/test/helpers/terraform-config-json/variables.json +++ b/test/helpers/terraform-config-json/variables.json @@ -47,9 +47,13 @@ "foo": "${var.account}_bar", "bar": { "baz": 1, - "${(var.account)}": 2, - "${(format(\"key_prefix_%s\", local.foo))}": 3, - "\"prefix_${var.account}:${var.user}_suffix\"": "interpolation" + "${var.account}": 2, + "${format(\"key_prefix_%s\", local.foo)}": 3, + "prefix_${var.account}:${var.user}_suffix": "interpolation", + "${var.start}-mid-${var.end}": 4, + "a:b": 5, + "123": 6, + "${var.x + 1}": 7 }, "tuple": ["${local.foo}"], "empty_tuple": [] diff --git a/test/helpers/terraform-config/variables.tf b/test/helpers/terraform-config/variables.tf index 9f0d6c6b..d6448b94 100644 --- a/test/helpers/terraform-config/variables.tf +++ b/test/helpers/terraform-config/variables.tf @@ -10,9 +10,13 @@ locals { baz : 1 (var.account) : 2 (format("key_prefix_%s", local.foo)) : 3 - "prefix_${var.account}:${var.user}_suffix":"interpolation", + "prefix_${var.account}:${var.user}_suffix" : "interpolation", + "${var.start}-mid-${var.end}" : 4 + "a:b" : 5 + 123 : 6 + (var.x + 1) : 7 } - tuple = [local.foo] + tuple = [local.foo] empty_tuple = [] } @@ -76,7 +80,7 @@ locals { for_whitespace = { for i in [1, 2, 3] : i => - i ... + i... } }