diff --git a/pycep/bicep.lark b/pycep/bicep.lark index bc43734..967aab2 100644 --- a/pycep/bicep.lark +++ b/pycep/bicep.lark @@ -49,7 +49,7 @@ module_path: QUOTED_INTERPOLATION // loops -loop: "[" "for" (loop_array | loop_array_index | loop_object | loop_range) ":" ["if" "(" value_brackets ")"] value_brackets "]" +loop: "[" _CPP_COMMENT_NL? "for" (loop_array | loop_array_index | loop_object | loop_range) ":" ["if" "(" value_brackets ")"] value_brackets _CPP_COMMENT_NL? "]" loop_array: STRING "in" value_brackets diff --git a/tests/examples/loop/array-w-newline/main.bicep b/tests/examples/loop/array-w-newline/main.bicep new file mode 100644 index 0000000..bb4387a --- /dev/null +++ b/tests/examples/loop/array-w-newline/main.bicep @@ -0,0 +1,92 @@ +// variable +var resourceAccessRules = [ + for (id, item) in networkAccess_resource: { + tenantId: az.subscription().tenantId + resourceId: networkAccess_resource[item] + } +] + +// output +output deployedNSGs array = [ + for (name, i) in orgNames: { + orgName: name + nsgName: nsg[i].name + resourceId: nsg[i].id + } +] + +// resource +/* +resource storageAccount_resource 'Microsoft.Storage/storageAccounts@2023-05-01' = { + name: storageAccountName + location: location + tags: tags + sku: { + name: storageAccountType + } + kind: storageAccountKind + properties: { + accessTier: storageAccountAccessTier + minimumTlsVersion: 'TLS1_2' + supportsHttpsTrafficOnly: true + allowBlobPublicAccess: false + allowCrossTenantReplication: true + encryption: { + keySource: 'Microsoft.Storage' + requireInfrastructureEncryption: false + services: { + blob: { + enabled: true + keyType: 'Account' + } + file: { + enabled: true + keyType: 'Account' + } + queue: { + enabled: true + keyType: 'Account' + } + table: { + enabled: true + keyType: 'Account' + } + } + } + isHnsEnabled: storageAccountHnsEnabled + isLocalUserEnabled: false + isNfsV3Enabled: false + isSftpEnabled: false + keyPolicy: { + keyExpirationPeriodInDays: 90 + } + largeFileSharesState: 'Disabled' + publicNetworkAccess: 'Enabled' + networkAcls: { + resourceAccessRules: networkAccess_resource == [''] || networkAccess_resource == [] ? [] : resourceAccessRules + bypass: 'AzureServices' + defaultAction: networkAccess_default + virtualNetworkRules: empty(networkingResourceGroupName) && empty(virtualNetworkName) && empty(virtualNetworkSubnetName) + ? [] + : [ + { + id: virtualNetwork_resource::subnet.id + action: 'Allow' + } + ] + } + } +} +*/ + +// module +module sqlLogicalServer 'sql-logical-server.bicep' = [ + for (sqlLogicalServer, index) in sqlLogicalServers: { + name: 'sqlLogicalServer-${index}' + params: { + sqlLogicalServer: union(defaultSqlLogicalServerProperties, sqlLogicalServer) + password: sqlLogicalServer.passwordFromKeyVault.secretName + tags: union(tags, union(defaultSqlLogicalServerProperties, sqlLogicalServer).tags) + } + } +] diff --git a/tests/examples/loop/array-w-newline/result.json b/tests/examples/loop/array-w-newline/result.json new file mode 100644 index 0000000..ea45b54 --- /dev/null +++ b/tests/examples/loop/array-w-newline/result.json @@ -0,0 +1,167 @@ +{ + "globals": { + "scope": { + "value": "resourceGroup" + } + }, + "variables": { + "resourceAccessRules": { + "decorators": [], + "value": { + "loop_type": { + "type": "array_index", + "detail": { + "item_name": "id", + "index_name": "item", + "array_name": "networkAccess_resource" + } + }, + "condition": null, + "config": { + "tenantId": { + "function": { + "type": "subscription", + "parameters": { + "subscription_id": null + }, + "property_name": "tenantId" + } + }, + "resourceId": { + "operator": { + "type": "index_accessor", + "operands": { + "operand_1": "networkAccess_resource", + "operand_2": "item" + } + } + } + } + } + } + }, + "outputs": { + "deployedNSGs": { + "decorators": [], + "type": "array", + "value": { + "loop_type": { + "type": "array_index", + "detail": { + "item_name": "name", + "index_name": "i", + "array_name": "orgNames" + } + }, + "condition": null, + "config": { + "orgName": "name", + "nsgName": { + "operator": { + "type": "property_accessor", + "operands": { + "operand_1": { + "operator": { + "type": "index_accessor", + "operands": { + "operand_1": "nsg", + "operand_2": "i" + } + } + }, + "operand_2": "name" + } + } + }, + "resourceId": { + "operator": { + "type": "property_accessor", + "operands": { + "operand_1": { + "operator": { + "type": "index_accessor", + "operands": { + "operand_1": "nsg", + "operand_2": "i" + } + } + }, + "operand_2": "id" + } + } + } + } + } + } + }, + "modules": { + "sqlLogicalServer": { + "decorators": [], + "type": "local", + "detail": { + "full": "sql-logical-server.bicep", + "path": "sql-logical-server.bicep" + }, + "config": { + "loop_type": { + "type": "array_index", + "detail": { + "item_name": "sqlLogicalServer", + "index_name": "index", + "array_name": "sqlLogicalServers" + } + }, + "condition": null, + "config": { + "name": "sqlLogicalServer-${index}", + "params": { + "sqlLogicalServer": { + "function": { + "type": "union", + "parameters": { + "arg_1": "defaultSqlLogicalServerProperties", + "arg_2": "sqlLogicalServer" + } + } + }, + "password": { + "operator": { + "type": "property_accessor", + "operands": { + "operand_1": "sqlLogicalServer", + "operand_2": { + "operator": { + "type": "property_accessor", + "operands": { + "operand_1": "passwordFromKeyVault", + "operand_2": "secretName" + } + } + } + } + } + }, + "tags": { + "function": { + "type": "union", + "parameters": { + "arg_1": "tags", + "arg_2": { + "function": { + "type": "union", + "parameters": { + "arg_1": "defaultSqlLogicalServerProperties", + "arg_2": "sqlLogicalServer" + }, + "property_name": "tags" + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/test_parse_loop.py b/tests/test_parse_loop.py index 48cfa65..07b4930 100644 --- a/tests/test_parse_loop.py +++ b/tests/test_parse_loop.py @@ -72,3 +72,16 @@ def test_parse_loop_range() -> None: # then assert_that(result).is_equal_to(expected_result) + + +def test_parse_array_w_newline() -> None: + # given + sub_dir_path = EXAMPLES_DIR / "array-w-newline" + file_path = sub_dir_path / "main.bicep" + expected_result = json.loads((sub_dir_path / "result.json").read_text()) + + # when + result = BICEP_PARSER.parse(file_path=file_path) + + # then + assert_that(result).is_equal_to(expected_result)