defmodule Wongi.Engine.WME do
@moduledoc "A single fact."
alias Wongi.Engine.DSL.Var
@type t() :: %__MODULE__{
subject: any(),
predicate: any(),
object: any()
}
@type fact() :: t() | {term(), term(), term()}
defstruct [:subject, :predicate, :object]
@doc false
def new(subject, predicate, object) do
%__MODULE__{
subject: subject,
predicate: predicate,
object: object
}
end
@doc false
def new({s, p, o}) do
%__MODULE__{
subject: s,
predicate: p,
object: o
}
end
def new([s, p, o]) do
%__MODULE__{
subject: s,
predicate: p,
object: o
}
end
@doc false
def template(s, p, o) do
new(
if(dynamic?(s), do: :_, else: s),
if(dynamic?(p), do: :_, else: p),
if(dynamic?(o), do: :_, else: o)
)
end
@doc false
def template({s, p, o}) do
new(
if(dynamic?(s), do: :_, else: s),
if(dynamic?(p), do: :_, else: p),
if(dynamic?(o), do: :_, else: o)
)
end
@doc false
defguard wild?(x) when x == :_
@doc false
defguard template?(wme)
when is_map(wme) and
(wild?(:erlang.map_get(:subject, wme)) or
wild?(:erlang.map_get(:predicate, wme)) or
wild?(:erlang.map_get(:object, wme)))
@doc false
defguard root?(wme)
when is_map(wme) and
wild?(:erlang.map_get(:subject, wme)) and
wild?(:erlang.map_get(:predicate, wme)) and
wild?(:erlang.map_get(:object, wme))
@doc false
def dynamic?(%Var{}), do: true
def dynamic?(:_), do: true
def dynamic?(_), do: false
@doc false
def index_pattern(template) do
[:object, :predicate, :subject]
|> Enum.reduce({[], []}, fn field, {fields, values} = acc ->
case template[field] do
:_ -> acc
value -> {[field | fields], [value | values]}
end
end)
end
@spec fetch(t(), :subject | :predicate | :object) :: any()
def fetch(%__MODULE__{} = wme, field), do: Map.fetch(wme, field)
defimpl Inspect do
import Inspect.Algebra
def inspect(%@for{subject: s, predicate: p, object: o}, opts) do
concat([
"WME.new(",
to_doc(s, opts),
", ",
to_doc(p, opts),
", ",
to_doc(o, opts),
")"
])
end
end
end