Current section
Files
Jump to
Current section
Files
lib/guesswork/ast/one_of.ex
defmodule Guesswork.Ast.OneOf do
@moduledoc """
Binds a value to an `Enumerable` of possible values.
Note that, when created, the bound value should always be a variable.
However, in the process of substitution it may become a concrete value, in
which case, on resolution, the stored value is checked against the stream.
It is invalid for the stream to produce a variable.
"""
defstruct [:binding, :values]
alias Guesswork.Answer
alias Guesswork.Ast.Entity
alias Guesswork.Ast.Statement.Opts
alias Guesswork.Ast.Variable
alias Guesswork.Telemetry.EventHandler
@type t() :: %__MODULE__{binding: Variable.t() | Entity.t(), values: Enumerable.t()}
@spec new(Variable.t(), Enumerable.t()) :: t()
def new(variable, stream), do: %__MODULE__{binding: variable, values: stream}
defimpl Guesswork.Ast.Term do
alias Guesswork.Ast.OneOf
def concrete?(%OneOf{binding: %Variable{}}), do: false
def concrete?(_), do: true
def get_variables(%OneOf{binding: %Variable{} = variable}), do: [variable]
def get_variables(_), do: []
end
defimpl Guesswork.Ast.Statement do
alias Guesswork.Ast.OneOf
def find_statements(statement, fun) do
if fun.(statement) do
[statement]
else
[]
end
end
def simplify(statement) do
statement
end
def substitute(%OneOf{binding: binding} = statement, env) do
case Map.fetch(env, binding) do
:error -> statement
{:ok, value} -> %OneOf{statement | binding: value}
end
end
def resolve(
%OneOf{binding: %Variable{} = var, values: stream},
%Opts{query_id: query_id, negated: negated}
) do
stream
|> Stream.filter(fn
%Variable{} -> false
_ -> true
end)
|> Stream.map(fn val ->
:telemetry.execute(EventHandler.assignment_event_name(), %{count: 1}, %{
query_id: query_id
})
{:ok, answer} = Answer.new(query_id) |> Answer.put_binding(var, val, negated)
answer
end)
end
def resolve(%OneOf{binding: value, values: stream}, %Opts{
query_id: query_id,
negated: negated
}) do
stream
|> Stream.filter(fn
%Variable{} -> false
val -> val == value != negated
end)
|> Stream.map(fn _ ->
:telemetry.execute(EventHandler.assignment_event_name(), %{count: 1}, %{
query_id: query_id
})
Answer.new(query_id)
end)
end
end
end