From fc4870c982dc29554d738e23631d60073d89d943 Mon Sep 17 00:00:00 2001 From: Ian Shearin Date: Tue, 12 Sep 2023 16:58:32 -0700 Subject: [PATCH] Allow expressions in string interpolation HCL allows for any expression in a string interpolation. This updates the grammar and transformer to match, allowing for arbitrarily-deep nesting. --- hcl2/hcl2.lark | 9 ++++----- hcl2/transformer.py | 6 ++++++ .../locals_embedded_nested_interpolation.json | 7 +++++++ .../locals_embedded_multi_function_nested.tf | 2 +- .../locals_embedded_nested_interpolation.tf | 3 +++ 5 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 test/helpers/terraform-config-json/locals_embedded_nested_interpolation.json create mode 100644 test/helpers/terraform-config/locals_embedded_nested_interpolation.tf diff --git a/hcl2/hcl2.lark b/hcl2/hcl2.lark index b9e453c..60c628a 100644 --- a/hcl2/hcl2.lark +++ b/hcl2/hcl2.lark @@ -1,7 +1,7 @@ start : body body : (new_line_or_comment? (attribute | block))* new_line_or_comment? attribute : identifier "=" expression -block : identifier (identifier | STRING_LIT)* new_line_or_comment? "{" body "}" +block : identifier (identifier | string_lit)* new_line_or_comment? "{" body "}" new_line_and_or_comma: new_line_or_comment | "," | "," new_line_or_comment new_line_or_comment: ( /\n/ | /#.*\n/ | /\/\/.*\n/ )+ @@ -20,7 +20,7 @@ binary_term : binary_operator new_line_or_comment? expression expr_term : "(" new_line_or_comment? expression new_line_or_comment? ")" | float_lit | int_lit - | STRING_LIT + | string_lit | tuple | object | function_call @@ -35,10 +35,9 @@ expr_term : "(" new_line_or_comment? expression new_line_or_comment? ")" | for_object_expr -STRING_LIT : "\"" (STRING_CHARS | INTERPOLATION)* "\"" +string_lit : "\"" (STRING_CHARS | interpolation)* "\"" STRING_CHARS : /(?:(?!\${)([^"\\]|\\.))+/+ // any character except '"" unless inside a interpolation string -NESTED_INTERPOLATION : "${" /[^}]+/ "}" -INTERPOLATION : "${" (/(?:(?!\${)([^}]))+/ | NESTED_INTERPOLATION)+ "}" +interpolation : "${" expression "}" int_lit : DECIMAL+ !float_lit: DECIMAL+ "." DECIMAL+ (EXP_MARK DECIMAL+)? diff --git a/hcl2/transformer.py b/hcl2/transformer.py index 6f2d4f3..e41f9c9 100644 --- a/hcl2/transformer.py +++ b/hcl2/transformer.py @@ -41,6 +41,12 @@ def float_lit(self, args: List) -> float: def int_lit(self, args: List) -> int: return int("".join([str(arg) for arg in args])) + def string_lit(self, args: List) -> str: + return '"' + "".join([str(arg) for arg in args]) + '"' + + def interpolation(self, args: List) -> str: + return "".join([self.to_string_dollar(arg) for arg in args]) + def expr_term(self, args: List) -> Any: args = self.strip_new_line_tokens(args) diff --git a/test/helpers/terraform-config-json/locals_embedded_nested_interpolation.json b/test/helpers/terraform-config-json/locals_embedded_nested_interpolation.json new file mode 100644 index 0000000..50078fb --- /dev/null +++ b/test/helpers/terraform-config-json/locals_embedded_nested_interpolation.json @@ -0,0 +1,7 @@ +{ + "locals": [ + { + "nested_interpolation": "tags: ${join(\";\", ['names:${join(\",\", [\\'name:${example}\\'])}'])}" + } + ] +} diff --git a/test/helpers/terraform-config/locals_embedded_multi_function_nested.tf b/test/helpers/terraform-config/locals_embedded_multi_function_nested.tf index 404710f..c6b126f 100644 --- a/test/helpers/terraform-config/locals_embedded_multi_function_nested.tf +++ b/test/helpers/terraform-config/locals_embedded_multi_function_nested.tf @@ -2,5 +2,5 @@ # both values should evaluate to the same values locals { multi_function = substr(split("-", "us-west-2")[0], 0, 1) - multi_function_embedded = substr(split("-", "us-west-2")[0], 0, 1) + multi_function_embedded = "${substr(split("-", "us-west-2")[0], 0, 1)}" } diff --git a/test/helpers/terraform-config/locals_embedded_nested_interpolation.tf b/test/helpers/terraform-config/locals_embedded_nested_interpolation.tf new file mode 100644 index 0000000..598b089 --- /dev/null +++ b/test/helpers/terraform-config/locals_embedded_nested_interpolation.tf @@ -0,0 +1,3 @@ +locals { + nested_interpolation = "tags: ${join(";", ["names:${join(",", ["name:${example}"])}"])}" +}