scripts/Interpolate-Attributes.ps1

function Interpolate-Attributes {
  [CmdletBinding()]
  param (
    [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
    [hashtable]$config
)
  process {
      $json = $config | ConvertTo-Json -Depth 99 -Compress
      $regex = [regex]'{{(?<subst>(?:(?!{{).)+?)}}'
      $counter = 0
      "Interpolate-Attributes:In:$($counter):`$json:$json" | f-log-dbg

      do {
        foreach ($substitute in (Get-Matches $json $regex 'subst')) {
          "Interpolate-Attributes:$($counter):`$substitute:$substitute" | f-log-dbg
          if ($substitute -match '(?ms)^\$\((?<eval>.*)\)$') {
            "Interpolate-Attributes:$($counter):`$eval:{$($matches.eval)}" | f-log-dbg
            $value = "`"$($matches.eval)`"" | ConvertFrom-Json | Invoke-Expression
          } elseif ($substitute -match '(?ms)^(?<filter>\$\S+?):(?<input>.*)$') {
            "Interpolate-Attributes:$($counter):`$filter:{$($matches.filter)}:`$input:{$($matches.input)}" | f-log-dbg
            $value = "`"$($matches.input)`"" | ConvertFrom-Json | & f-$($matches.filter)
            "Interpolate-Attributes:$($counter):`$value:{$value}" | f-log-dbg
          } else {
            $value = Invoke-Expression "`$config.attributes.$substitute" -ErrorAction Stop
            if ($regex.Match($value).Success) {
                continue;
            }
          }
          $value = $value | f-null | ForEach-Object {(ConvertTo-Json $_ -Compress -Depth 99).Trim('"')}
          "Interpolate-Attributes:$($counter):`$value:{$value}" | f-log-dbg
          $json = $json.Replace("{{$substitute}}", "$value")
        }
        if ($counter++ -ge ${global:pwshake-context}.options.max_depth) {
          throw "Circular reference detected for substitutions: $($regex.Matches($json) | Sort-Object -Property Value)"
        }
        "Interpolate-Attributes:$($counter):`$json:`n$json" | f-log-dbg
        $config = ConvertFrom-Yaml $json
        $json = ConvertTo-Json $config -Depth 99 -Compress
      } while ($regex.Match($json).Success)

      return ConvertFrom-Yaml $json
  }
}