checkpoint failing test after fixing tests checkpoint checkpoint checkpoint re-work asd checkpoint checkpoint checkpoint mix proj checkpoint mix first parser impl checkpoint fix tests re-org parser checkpoint strings fix multiline strings tuples checkpoint maps checkpoint checkpoint checkpoint checkpoint fix weird eof expression parse error checkpoint before typing checkpoint checpoint checkpoint checkpoint checkpoint ids in primitive types checkpoint checkpoint fix tests initial annotation checkpoint checkpoint checkpoint union subtyping conventions refactor - split typer typing tuples checkpoint test refactor checkpoint test refactor parsing atoms checkpoint atoms wip lists checkpoint typing lists checkopint checkpoint wip fixing correct list typing map discussion checkpoint map basic typing fix tests checkpoint checkpoint checkpoint checkpoint fix condition typing fix literal keys in map types checkpoint union types checkpoint union type checkpoint row types discussion & bidirectional typecheck checkpoint basic lambdas checkpoint lambdas typing application wip function application checkpoint checkpoint checkpoint cduce checkpoint checkpoint checkpoint checkpoint checkpoint checkpoint checkpoint
125 lines
4.4 KiB
Elixir
125 lines
4.4 KiB
Elixir
defmodule Tilly.BDD.Node do
|
|
@moduledoc """
|
|
Defines the structure of BDD nodes and provides basic helper functions.
|
|
|
|
BDD nodes can be one of the following Elixir terms:
|
|
- `true`: Represents the universal set BDD.
|
|
- `false`: Represents the empty set BDD.
|
|
- `{:leaf, leaf_value_id}`: Represents a leaf node.
|
|
`leaf_value_id`'s interpretation depends on the specific BDD's `ops_module`.
|
|
- `{:split, element_id, positive_child_id, ignore_child_id, negative_child_id}`:
|
|
Represents an internal decision node.
|
|
`element_id` is the value being split upon.
|
|
`positive_child_id`, `ignore_child_id`, `negative_child_id` are IDs of other BDD nodes.
|
|
"""
|
|
|
|
@typedoc "A BDD node representing the universal set."
|
|
@type true_node :: true
|
|
|
|
@typedoc "A BDD node representing the empty set."
|
|
@type false_node :: false
|
|
|
|
@typedoc "A BDD leaf node."
|
|
@type leaf_node(leaf_value) :: {:leaf, leaf_value}
|
|
|
|
@typedoc "A BDD split node."
|
|
@type split_node(element, node_id) ::
|
|
{:split, element, node_id, node_id, node_id}
|
|
|
|
@typedoc "Any valid BDD node structure."
|
|
@type t(element, leaf_value, node_id) ::
|
|
true_node()
|
|
| false_node()
|
|
| leaf_node(leaf_value)
|
|
| split_node(element, node_id)
|
|
|
|
# --- Smart Constructors (Low-Level) ---
|
|
|
|
@doc "Creates a true BDD node."
|
|
@spec mk_true() :: true_node()
|
|
def mk_true, do: true
|
|
|
|
@doc "Creates a false BDD node."
|
|
@spec mk_false() :: false_node()
|
|
def mk_false, do: false
|
|
|
|
@doc "Creates a leaf BDD node."
|
|
@spec mk_leaf(leaf_value :: any()) :: leaf_node(any())
|
|
def mk_leaf(leaf_value_id), do: {:leaf, leaf_value_id}
|
|
|
|
@doc "Creates a split BDD node."
|
|
@spec mk_split(
|
|
element_id :: any(),
|
|
positive_child_id :: any(),
|
|
ignore_child_id :: any(),
|
|
negative_child_id :: any()
|
|
) :: split_node(any(), any())
|
|
def mk_split(element_id, positive_child_id, ignore_child_id, negative_child_id) do
|
|
{:split, element_id, positive_child_id, ignore_child_id, negative_child_id}
|
|
end
|
|
|
|
# --- Predicates ---
|
|
|
|
@doc "Checks if the node is a true node."
|
|
@spec is_true?(node :: t(any(), any(), any())) :: boolean()
|
|
def is_true?(true), do: true
|
|
def is_true?(_other), do: false
|
|
|
|
@doc "Checks if the node is a false node."
|
|
@spec is_false?(node :: t(any(), any(), any())) :: boolean()
|
|
def is_false?(false), do: true
|
|
def is_false?(_other), do: false
|
|
|
|
@doc "Checks if the node is a leaf node."
|
|
@spec is_leaf?(node :: t(any(), any(), any())) :: boolean()
|
|
def is_leaf?({:leaf, _value}), do: true
|
|
def is_leaf?(_other), do: false
|
|
|
|
@doc "Checks if the node is a split node."
|
|
@spec is_split?(node :: t(any(), any(), any())) :: boolean()
|
|
def is_split?({:split, _el, _p, _i, _n}), do: true
|
|
def is_split?(_other), do: false
|
|
|
|
# --- Accessors ---
|
|
|
|
@doc """
|
|
Returns the value of a leaf node.
|
|
Raises an error if the node is not a leaf node.
|
|
"""
|
|
@spec value(leaf_node :: leaf_node(any())) :: any()
|
|
def value({:leaf, value_id}), do: value_id
|
|
def value(other), do: raise(ArgumentError, "Not a leaf node: #{inspect(other)}")
|
|
|
|
@doc """
|
|
Returns the element of a split node.
|
|
Raises an error if the node is not a split node.
|
|
"""
|
|
@spec element(split_node :: split_node(any(), any())) :: any()
|
|
def element({:split, element_id, _, _, _}), do: element_id
|
|
def element(other), do: raise(ArgumentError, "Not a split node: #{inspect(other)}")
|
|
|
|
@doc """
|
|
Returns the positive child ID of a split node.
|
|
Raises an error if the node is not a split node.
|
|
"""
|
|
@spec positive_child(split_node :: split_node(any(), any())) :: any()
|
|
def positive_child({:split, _, p_child_id, _, _}), do: p_child_id
|
|
def positive_child(other), do: raise(ArgumentError, "Not a split node: #{inspect(other)}")
|
|
|
|
@doc """
|
|
Returns the ignore child ID of a split node.
|
|
Raises an error if the node is not a split node.
|
|
"""
|
|
@spec ignore_child(split_node :: split_node(any(), any())) :: any()
|
|
def ignore_child({:split, _, _, i_child_id, _}), do: i_child_id
|
|
def ignore_child(other), do: raise(ArgumentError, "Not a split node: #{inspect(other)}")
|
|
|
|
@doc """
|
|
Returns the negative child ID of a split node.
|
|
Raises an error if the node is not a split node.
|
|
"""
|
|
@spec negative_child(split_node :: split_node(any(), any())) :: any()
|
|
def negative_child({:split, _, _, _, n_child_id}), do: n_child_id
|
|
def negative_child(other), do: raise(ArgumentError, "Not a split node: #{inspect(other)}")
|
|
end
|