Escaping runtime values in a macro with `^`

I’ve been building a tool that compiles elixir expressions into a “workflow” a directed acyclic graph of steps, conditions, and other nodes. Some examples in a livebook here. The advantage of doing this is it allows for composition of functionality at runtime - with varying levels of potential user defined logic. Should be handy for data pipelines, rule based expert systems or other runtime dependent workflows.

A big part of expert systems is to build rules at runtime, but to do this I need to be able to inject runtime values into a macro (at least as implemented - if there’s a better way to extract atomic conditionals in a function I’m all ears).

So the idea is supporting ^ as an escape value like Ecto and Explorer do, but I barely understand Macros and have been struggling to figure out how to access the runtime values in this situation.

test "escapes runtime values with '^'" do
    some_values = [:potato, :ham, :tomato]

    escaped_rule = Dagger.rule(
      name: "escaped_rule",
      condition: fn val when val in ^some_values -> true end,
      reaction: "food"
    )

    assert match?(%Rule{}, escaped_rule)
    assert Rule.check(escaped_rule, :potato)
end

I want the left hand side of this rule to build a function such as:

fn val when val in [:potato, :ham, :tomato] -> true end

I noticed Jose implemented something similar in Explorer recently: explorer/lib/explorer/query.ex at v0.5.5 · elixir-explorer/explorer · GitHub but I don’t quite understand how it works yet.

I’ve tried a few variations such as:

check =
  Macro.prewalk(lhs, fn
    {:^, _meta, [{bind, _, _} = expr]} = ast ->
      Macro.Env.has_var?(context, {bind, nil}) |> IO.inspect(label: "has var?") # true
      runtime_value = Macro.Env.vars(context) |> IO.inspect() # [some_values: nil]

      if runtime_value do
        var = Macro.unique_var(bind, __MODULE__)
        quote do: unquote(var)
      else
        ast
      end

    ast ->
      ast
  end)

Can anyone more familiar with macros and/or escaping runtime values help me understand how this works or how you’d approach this problem? Any feedback is appreciated.

Thanks.