lib/wongi/engine/beta/assign.ex

defmodule Wongi.Engine.Beta.Assign do
  @moduledoc false
  @type t() :: %__MODULE__{}
  defstruct [:ref, :parent_ref, :name, :value]

  def new(parent_ref, name, value) do
    %__MODULE__{
      ref: make_ref(),
      parent_ref: parent_ref,
      name: name,
      value: value
    }
  end

  def evaluate(fun_or_static, token, rete)
  def evaluate(fun, _, _) when is_function(fun, 0), do: fun.()
  def evaluate(fun, token, _) when is_function(fun, 1), do: fun.(token)
  def evaluate(fun, token, rete) when is_function(fun, 2), do: fun.(token, rete)
  def evaluate(value, _, _), do: value

  defimpl Wongi.Engine.Beta do
    alias Wongi.Engine.Beta.Common
    alias Wongi.Engine.Rete
    alias Wongi.Engine.Token

    def ref(%@for{ref: ref}), do: ref
    def parent_refs(%@for{parent_ref: parent_ref}), do: [parent_ref]

    def seed(%@for{name: name, value: value} = node, beta, rete) do
      tokens = Rete.tokens(rete, node)

      Enum.reduce(tokens, rete, fn token, rete ->
        assignments = %{name => @for.evaluate(value, token, rete)}
        Common.beta_activate([beta], &Token.new(&1, [token], nil, assignments), rete)
      end)
    end

    def equivalent?(%@for{name: name, value: value}, %@for{name: name, value: value}, _rete),
      do: true

    def equivalent?(_, _, _), do: false

    @spec alpha_activate(
            Wongi.Engine.Beta.Assign.t(),
            Wongi.Engine.WME.t(),
            Wongi.Engine.Rete.t()
          ) :: no_return()
    defdelegate alpha_activate(assign, alpha, rete), to: Wongi.Engine.Beta.NonAlphaListening

    @spec alpha_deactivate(
            Wongi.Engine.Beta.Assign.t(),
            Wongi.Engine.WME.t(),
            Wongi.Engine.Rete.t()
          ) :: no_return()
    defdelegate alpha_deactivate(assign, alpha, rete), to: Wongi.Engine.Beta.NonAlphaListening

    def beta_activate(%@for{name: name, value: value} = node, token, rete) do
      rete = Rete.add_token(rete, token)
      assignments = %{name => @for.evaluate(value, token, rete)}
      betas = Rete.beta_subscriptions(rete, node)
      Common.beta_activate(betas, &Token.new(&1, [token], nil, assignments), rete)
    end

    def beta_deactivate(node, token, rete) do
      rete =
        rete
        |> Rete.remove_token(token)

      rete
      |> Rete.beta_subscriptions(node)
      |> Common.beta_deactivate(token, rete)
    end
  end
end