scripts/Interpolate-Evals.ps1

function Interpolate-Evals {
  [CmdletBinding()]
  param (
    [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
    [hashtable]$step,

    [Parameter(Position = 1, Mandatory = $false)]
    [regex]$regex = '\$\[(?<key>.*?)\[(?<eval>.*?)\]\]',

    [Parameter(Position = 2, Mandatory = $false)]
    [string]$groupKey = 'eval',

    [Parameter(Position = 3, Mandatory = $false)]
    [hashtable]$config = (Coalesce (Peek-Config), @{}),

    [Parameter(Position = 4, Mandatory = $false)]
    [string]$template_key = $null
  )
  process {
    "Interpolate-Evals:In:$(@{'$_'=$_;'$template_key'="$template_key"} | ConvertTo-Yaml)" | f-log-dbg

    $context_tmp = $step['$context']
    if (-not $template_key) {
      $template_key = $context_tmp.template_key
    }

    try {
      if ($template_key) {
        New-Variable -Name $template_key -Value $step -Force
      }
      if ($context_tmp) {
        $step['$context'] = $null
      }

      $json = $step | f-ctj
      "Interpolate-Evals:In:`$json = $json" | f-log-dbg
      $counter = 0
      do {
        foreach ($match in $regex.Matches($json)) {
          $eval = $match.Groups[$groupKey].Value
          $key = $match.Groups['key'].Value
          $subst = $match.Groups[0].Value
          "Interpolate-Evals:Groups:`n$(@{subst=$subst;eval=$eval;key=$key} | ConvertTo-Yaml))" | f-log-dbg
          if (($key) -and ($step.$($key) -is [string]) -and ($regex.Match($step.$($key)).Success)) {
            # it might be evaluated later
            continue
          }
          $value = Invoke-Expression ("`"$eval`"" | ConvertFrom-Json)
          "Interpolate-Evals:Eval:$(@{'$value'=$value} | ConvertTo-Yaml)" | f-log-dbg
          if ($value -isnot [string]) {
            # assign complex values via json, the trick with ,@(...) is to pass an array as itself into the pipeline
            $value = ,@($value) | f-ctj
            $json = $json.Replace("`"$subst`"", $value) # <<< it migth be former string
          } else {
            $value = $value | f-escape-json
          }
          $json = $json.Replace($subst, $value)
          "Interpolate-Evals:Replace:`$value = $value" | f-log-dbg
          "Interpolate-Evals:Replace:`$json = $json" | f-log-dbg
        }

        if ($counter++ -ge ${global:pwshake-context}.options.max_depth) {
          throw "Circular reference detected for evaluations: $($regex.Matches($json) | Sort-Object -Property Value)"
        }

        $step = $json | ConvertFrom-Yaml
        if ($template_key) {
          Set-Variable -Name $template_key -Value $step -Force
        }
        $json = $step | f-ctj
      } while ($regex.Match($json).Success)
    }
    finally {
      if ($context_tmp) { $step['$context'] = $context_tmp }
    }

    "Interpolate-Evals:Out:$(@{'$step'=$step} | ConvertTo-Yaml)" | f-log-dbg
    return $step
  }
}