-module(match_test).
-include_lib("eunit/include/eunit.hrl").

eval(Content) -> eval(Content, []).

eval(Content, Initial) ->
  {Value, Binding, _, _} = elixir:eval(Content, Initial),
  {Value, Binding}.

no_assignment_test() ->
  {nil, []} = eval("").

% Var/assignment test
arithmetic_test() ->
  ?assertError({badmatch, _}, eval("-1 = 1")).

assignment_test() ->
  {1, [{a, 1}]} = eval("a = 1").

not_single_assignment_test() ->
  {2, [{a, 2}]} = eval("a = 1\na = 2\na"),
  {1, [{a, 1}]} = eval("{a,a} = {1,1}\na"),
  {2, [{a, 2}]} = eval("a = 1\n{^a,a} = {1,2}\na"),
  ?assertError({badmatch, _}, eval("{a,a} = {1,2}")),
  ?assertError({badmatch, _}, eval("{1 = a,a} = {1,2}")),
  ?assertError({badmatch, _}, eval("{a = 1,a} = {1,2}")),
  ?assertError({badmatch, _}, eval("a = 0;{a,a} = {1,2}")),
  ?assertError({badmatch, _}, eval("a = 0;{1 = a,a} = {1,2}")),
  ?assertError({badmatch, _}, eval("a = 1\n^a = 2")).

duplicated_assignment_on_module_with_tuple_test() ->
  F = fun() ->
    eval("defmodule Foo do\ndef v({a, _left}, {a, _right}), do: a\nend"),
    {1,_} = eval("Foo.v({1, :foo}, {1, :bar})"),
    ?assertError(function_clause, eval("Foo.v({1, :foo}, {2, :bar})"))
  end,
  test_helper:run_and_remove(F, ['Elixir.Foo']).

duplicated_assignment_on_module_with_list_test() ->
  F = fun() ->
    eval("defmodule Foo do\ndef v([ a, _left ], [ a, _right ]), do: a\nend"),
    {1,_} = eval("Foo.v([ 1, :foo ], [ 1, :bar ])"),
    ?assertError(function_clause, eval("Foo.v([ 1, :foo ], [ 2, :bar ])"))
  end,
  test_helper:run_and_remove(F, ['Elixir.Foo']).

multiline_assignment_test() ->
  {1, [{a, 1}]} = eval("a =\n1"),
  {1, [{a, 1}, {b, 1}]} = eval("a = 1\nb = 1").

multiple_assignment_test() ->
  {1, [{a, 1}, {b, 1}]} = eval("a = b = 1").

multiple_assignment_with_parens_test() ->
  {1, [{a, 1}, {b, 1}]} = eval("a = (b = 1)").

multiple_assignment_with_left_parens_test() ->
  {1, [{a, 1}, {b, 1}]} = eval("(a) = (b = 1)").

multiple_assignment_with_expression_test() ->
  {-4, [{a, -4}, {b, -4}]} = eval("a = (b = -(2 * 2))").

multiple_assignment_with_binding_expression_test() ->
  {3, [{a, 3}, {b, 1}]} = eval("a = (2 + b)", [{b, 1}]).

underscore_assignment_test() ->
  {1, []} = eval("_ = 1").

assignment_precedence_test() ->
  {_, [{x,{'__block__', _, [1,2,3]}}]} = eval("x = quote do\n1\n2\n3\nend").

% Tuples match
simple_tuple_test() ->
  {{}, _} = eval("a = {}"),
  {{1,2,3}, _} = eval("a = {1, 2, 3}"),
  {{1,2,3}, _} = eval("a = {1, 1 + 1, 3}"),
  {{1,{2},3}, _} = eval("a = {1, {2}, 3}").

tuple_match_test() ->
  {_, _} = eval("{1,2,3} = {1, 2, 3}"),
  ?assertError({badmatch, _}, eval("{1, 3, 2} = {1, 2, 3}")).

% Lists match
simple_list_test() ->
  {[], _} = eval("a = []"),
  {[1,2,3], _} = eval("a = [1, 2, 3]"),
  {[1,2,3], _} = eval("a = [1, 1 + 1, 3]"),
  {[1,[2],3], _} = eval("a = [1, [2], 3]"),
  {[1,{2},3], _} = eval("a = [1, {2}, 3]").

list_match_test() ->
  {_, _} = eval("[1, 2, 3] = [1, 2, 3]"),
  ?assertError({badmatch, _}, eval("[1, 3, 2] = [1, 2, 3]")).

list_vars_test() ->
  {[3,1], [{x,3}]} = eval("x = 1\n[x = x + 2, x]").

head_and_tail_test() ->
  {_,[{h,1},{t,[2,3]}]} = eval("[h|t] = [1,2,3]"),
  {_,[{h,2},{t,[3]}]} = eval("[1,h|t] = [1,2,3]"),
  {_,[{t,[3]}]} = eval("[1,2|t] = [1,2,3]"),
  {_,[{h,1}]} = eval("[h|[2,3]] = [1,2,3]"),
  {_,[{t,[2,3]}]} = eval("[+1|t] = [1,2,3]"),
  ?assertError({badmatch, _}, eval("[2,h|t] = [1,2,3]")).

% Keyword match

orrdict_match_test() ->
  {[{a,1},{b,2}], _} = eval("a = [a: 1, b: 2]").
  
% Function match

function_clause_test() ->
  F = fun() ->
    eval("defmodule Foo do\ndef a([{_k,_}=e|_]), do: e\nend"),
    {{foo,bar},_} = eval("Foo.a([{:foo,:bar}])")
  end,
  test_helper:run_and_remove(F, ['Elixir.Foo']).