Files
Adam d8719b6d48 Phases 1-7: Complete CljElixir compiler through Malli schema adapter
Bootstrap compiler (reader, analyzer, transformer, compiler, Mix plugin),
core protocols (16 protocols for Map/List/Tuple/BitString), PersistentVector
(bit-partitioned trie), domain tools (clojurify/elixirify), BEAM concurrency
(receive, spawn, GenServer), control flow & macros (threading, try/catch,
destructuring, defmacro with quasiquote/auto-gensym), and Malli schema
adapter (m/=> specs, auto @type, recursive schemas, cross-references).

537 compiler tests + 55 Malli unit tests + 15 integration tests = 607 total.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 10:38:22 -04:00

113 lines
2.9 KiB
Elixir

defmodule CljElixir.Equality do
@moduledoc """
Cross-type equality for CljElixir.
Handles the case where `(= [1 2 3] '(1 2 3))` should return true —
PersistentVector and list are both sequential types with the same elements.
"""
def equiv(a, b) when a === b, do: true
def equiv(%CljElixir.PersistentVector{} = a, %CljElixir.PersistentVector{} = b) do
a.cnt == b.cnt and CljElixir.PersistentVector.to_list(a) == CljElixir.PersistentVector.to_list(b)
end
def equiv(%CljElixir.PersistentVector{} = a, b) when is_list(b) do
CljElixir.PersistentVector.to_list(a) == b
end
def equiv(a, %CljElixir.PersistentVector{} = b) when is_list(a) do
a == CljElixir.PersistentVector.to_list(b)
end
def equiv(%CljElixir.SubVector{} = a, b) do
CljElixir.SubVector.sv_to_list(a) |> equiv_list(b)
end
def equiv(a, %CljElixir.SubVector{} = b) do
equiv_list(CljElixir.SubVector.sv_to_list(b), a)
end
def equiv(a, b), do: a == b
defp equiv_list(list, other) when is_list(other), do: list == other
defp equiv_list(list, %CljElixir.PersistentVector{} = pv) do
list == CljElixir.PersistentVector.to_list(pv)
end
defp equiv_list(_, _), do: false
end
defimpl Enumerable, for: CljElixir.PersistentVector do
def count(pv), do: {:ok, pv.cnt}
def member?(_pv, _value), do: {:error, __MODULE__}
def reduce(_pv, {:halt, acc}, _fun), do: {:halted, acc}
def reduce(pv, {:suspend, acc}, fun), do: {:suspended, acc, &reduce(pv, &1, fun)}
def reduce(pv, {:cont, acc}, fun) do
list = CljElixir.PersistentVector.to_list(pv)
Enumerable.List.reduce(list, {:cont, acc}, fun)
end
def slice(pv) do
size = pv.cnt
{:ok, size, &slice_fun(pv, &1, &2, &3)}
end
defp slice_fun(pv, start, length, step) do
start..(start + (length - 1) * step)//step
|> Enum.map(fn i -> CljElixir.PersistentVector.pv_nth(pv, i) end)
end
end
defimpl Collectable, for: CljElixir.PersistentVector do
def into(pv) do
collector_fun = fn
acc, {:cont, elem} -> CljElixir.PersistentVector.pv_conj(acc, elem)
acc, :done -> acc
_acc, :halt -> :ok
end
{pv, collector_fun}
end
end
defimpl Enumerable, for: Tuple do
def count(t), do: {:ok, tuple_size(t)}
def member?(_t, _value), do: {:error, __MODULE__}
def reduce(_t, {:halt, acc}, _fun), do: {:halted, acc}
def reduce(t, {:suspend, acc}, fun), do: {:suspended, acc, &reduce(t, &1, fun)}
def reduce(t, {:cont, acc}, fun) do
list = Tuple.to_list(t)
Enumerable.List.reduce(list, {:cont, acc}, fun)
end
def slice(t) do
size = tuple_size(t)
{:ok, size, &slice_fun(t, &1, &2, &3)}
end
defp slice_fun(t, start, length, step) do
start..(start + (length - 1) * step)//step
|> Enum.map(fn i -> elem(t, i) end)
end
end
defimpl Collectable, for: Tuple do
def into(t) do
collector_fun = fn
acc, {:cont, elem} -> :erlang.append_element(acc, elem)
acc, :done -> acc
_acc, :halt -> :ok
end
{t, collector_fun}
end
end