Skip to content

Commit 33aaaf1

Browse files
committed
fix: handle improper lists when generating patterns (closes #105)
1 parent 383753a commit 33aaaf1

File tree

4 files changed

+43
-1
lines changed

4 files changed

+43
-1
lines changed

lib/mneme/assertion/pattern.ex

+15
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,21 @@ defmodule Mneme.Assertion.Pattern do
3535
Pattern.new(exprs, guard: guard, notes: notes)
3636
end
3737

38+
@doc """
39+
Adds an improper tail onto a list pattern.
40+
"""
41+
def with_improper_tail(%Pattern{expr: list} = list_pattern, %Pattern{} = tail)
42+
when is_list(list) do
43+
%Pattern{
44+
expr: improper_tail_expr(list, tail.expr),
45+
guard: combine_guards(list_pattern.guard, tail.guard),
46+
notes: list_pattern.notes ++ tail.notes
47+
}
48+
end
49+
50+
defp improper_tail_expr([last], tail_expr), do: [{:|, [], [last, tail_expr]}]
51+
defp improper_tail_expr([x | xs], tail_expr), do: [x | improper_tail_expr(xs, tail_expr)]
52+
3853
defp combine_guards(nil, guard), do: guard
3954
defp combine_guards(guard, nil), do: guard
4055
defp combine_guards(g1, {_, meta, _} = g2), do: {:and, meta, [g2, g1]}

lib/mneme/assertion/pattern_builder.ex

+19-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,16 @@ defmodule Mneme.Assertion.PatternBuilder do
7575
defp do_to_patterns([], _, vars), do: {[Pattern.new([])], vars}
7676

7777
defp do_to_patterns(list, context, vars) when is_list(list) do
78-
{patterns, vars} = enum_to_patterns(list, context, vars)
78+
{patterns, vars} =
79+
case pop_tail(list) do
80+
{list, []} ->
81+
enum_to_patterns(list, context, vars)
82+
83+
{list, improper_tail} ->
84+
{patterns, vars} = enum_to_patterns(list, context, vars)
85+
{[tail_pattern | _], vars} = do_to_patterns(improper_tail, context, vars)
86+
{Enum.map(patterns, &Pattern.with_improper_tail(&1, tail_pattern)), vars}
87+
end
7988

8089
if List.ascii_printable?(list) do
8190
{patterns ++ [charlist_pattern(list, context)], vars}
@@ -474,4 +483,13 @@ defmodule Mneme.Assertion.PatternBuilder do
474483
name_i = :"#{name}#{i}"
475484
if(name_i in var_names, do: get_unique_name(name, var_names, i + 1), else: name_i)
476485
end
486+
487+
defp pop_tail([x | []]), do: {[x], []}
488+
489+
defp pop_tail([x | xs]) when is_list(xs) do
490+
{xs, tail} = pop_tail(xs)
491+
{[x | xs], tail}
492+
end
493+
494+
defp pop_tail([x | improper_tail]), do: {[x], improper_tail}
477495
end

test/mneme/assertion/pattern_builder_test.exs

+4
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ defmodule Mneme.Assertion.PatternBuilderTest do
6565
auto_assert ["[1, [:nested], 3]"] <- to_pattern_strings([1, [:nested], 3])
6666
end
6767

68+
test "improper lists" do
69+
auto_assert ["[:x | :y]"] <- to_pattern_strings([:x | :y])
70+
end
71+
6872
test "pins and guards" do
6973
ref = make_ref()
7074
auto_assert ["ref when is_reference(ref)"] <- to_pattern_strings(ref)

test_integration/basic_test.exs

+5
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ defmodule Mneme.Integration.BasicTest do
128128
auto_assert [ref] when is_reference(ref) <- l
129129
end
130130

131+
test "improper lists" do
132+
# y
133+
auto_assert [:x | :y] <- [:x | :y]
134+
end
135+
131136
test "charlists" do
132137
# y
133138
auto_assert [102, 111, 111] <- String.to_charlist("foo")

0 commit comments

Comments
 (0)