Current section
Files
Jump to
Current section
Files
lib/nodes/p_node.ex
defmodule Retex.Node.PNode do
@moduledoc """
Production node. This is like a production node in Rete algorithm. It is activated if all
the conditions in a rule are matching and contains the action that can be executed as consequence.
"""
defstruct type: :PNode, action: nil, id: nil, raw_action: nil, bindings: %{}, filters: []
def new(action, filters \\ [], labels \\ []) do
item = %__MODULE__{action: action, raw_action: action, filters: filters}
{%{item | id: Retex.hash(item)}, labels}
end
defimpl Retex.Protocol.Activation do
def activate(
neighbor = %{filters: filters},
%Retex{tokens: tokens, graph: graph, activations: activations} = rete,
_wme,
_bindings,
_tokens
) do
# A production node has only one parent
[parent | _] =
parents =
Graph.in_neighbors(graph, neighbor)
|> Enum.filter(fn node ->
Map.get(activations, node.id)
end)
tokens = Map.get(tokens, parent.id)
if Enum.all?(parents, &Map.get(activations, &1.id)) do
productions =
for token <- tokens do
if is_tuple(token) do
Retex.replace_bindings(neighbor, token)
else
Retex.replace_bindings(neighbor, token.bindings)
end
end
|> List.flatten()
|> Enum.uniq()
|> apply_filters(filters)
new_rete = %{
rete
| agenda: ([productions] ++ rete.agenda) |> List.flatten() |> Enum.uniq()
}
Retex.stop_traversal(new_rete, %{})
else
Retex.stop_traversal(rete, %{})
end
end
def active?(_, _) do
raise "Not implemented"
end
def apply_filters(nodes, filters) do
Enum.filter(nodes, fn node ->
Enum.reduce_while(filters, true, fn filter, _ ->
if test_pass?(node, filter), do: {:cont, true}, else: {:halt, false}
end)
end)
end
def test_pass?(%Retex.Node.PNode{bindings: bindings}, %Retex.Fact.Filter{
predicate: predicate,
value: value,
variable: variable
}) do
case Map.get(bindings, variable, :_undefined) do
:_undefined -> true
current_value -> apply(Kernel, predicate, [current_value, value])
end
end
end
end