1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
-module(function_test).
-include_lib("eunit/include/eunit.hrl").
eval(Content) ->
Quoted = elixir:'string_to_quoted!'(Content, 1, 1, <<"nofile">>, []),
{Value, Binding, _} = elixir:eval_forms(Quoted, [], elixir:env_for_eval([])),
{Value, lists:sort(Binding)}.
function_arg_do_end_test() ->
{3, _} = eval("if true do\n1 + 2\nend"),
{nil, _} = eval("if true do end").
function_stab_end_test() ->
{_, [{a, Fun3}]} = eval("a = fn -> 1 + 2 end"),
3 = Fun3().
function_stab_newlines_test() ->
{_, [{a, Fun3}]} = eval("a = fn\n->\n1 + 2\nend"),
3 = Fun3().
function_stab_many_test() ->
{_, [{a, Fun}]} = eval("a = fn\n{:foo, x} -> x\n{:bar, x} -> x\nend"),
1 = Fun({foo, 1}),
2 = Fun({bar, 2}).
function_stab_inline_test() ->
{_, [{a, Fun}]} = eval("a = fn {:foo, x} -> x; {:bar, x} -> x end"),
1 = Fun({foo, 1}),
2 = Fun({bar, 2}).
function_with_args_test() ->
{Fun, _} = eval("fn(a, b) -> a + b end"),
3 = Fun(1, 2).
function_with_kv_args_test() ->
{Fun, _} = eval("fn(a, [other: b, another: c]) -> a + b + c end"),
6 = Fun(1, [{other, 2}, {another, 3}]).
function_as_closure_test() ->
{_, [{a, Res1} | _]} = eval("b = 1; a = fn -> b + 2 end"),
3 = Res1().
function_apply_test() ->
{3, _} = eval("a = fn -> 3 end; apply a, []").
function_apply_with_args_test() ->
{3, _} = eval("a = fn b -> b + 2 end; apply a, [1]").
function_apply_and_clojure_test() ->
{3, _} = eval("b = 1; a = fn -> b + 2 end; apply a, []").
function_parens_test() ->
{0, _} = eval("(fn() -> 0 end).()"),
{1, _} = eval("(fn(1) -> 1 end).(1)"),
{3, _} = eval("(fn(1, 2) -> 3 end).(1, 2)"),
{0, _} = eval("(fn() -> 0 end).()"),
{1, _} = eval("(fn(1) -> 1 end).(1)"),
{3, _} = eval("(fn(1, 2) -> 3 end).(1, 2)").
%% Function calls
function_call_test() ->
{3, _} = eval("x = fn a, b -> a + b end\nx.(1, 2)").
function_call_without_arg_test() ->
{3, _} = eval("x = fn -> 2 + 1 end\nx.()").
function_call_do_end_test() ->
{[1, [{do, 2}, {'else', 3}]], _} = eval("x = fn a, b -> [a, b] end\nx.(1) do\n2\nelse 3\nend").
function_call_with_assignment_test() ->
{3, [{a, _}, {c, 3}]} = eval("a = fn x -> x + 2 end; c = a.(1)").
function_calls_with_multiple_expressions_test() ->
{26, _} = eval("a = fn a, b -> a + b end; a.((3 + 4 - 1), (2 * 10))").
function_calls_with_multiple_args_with_line_breaks_test() ->
{5, _} = eval("a = fn a, b -> a + b end; a.(\n3,\n2\n)").
function_calls_with_parenthesis_test() ->
{3, [{a, _}, {b, 1}]} = eval("a = (fn x -> x + 2 end).(b = 1)").
function_call_with_a_single_space_test() ->
{3, _} = eval("a = fn a, b -> a + b end; a. (1, 2)"),
{3, _} = eval("a = fn a, b -> a + b end; a .(1, 2)").
function_call_with_spaces_test() ->
{3, _} = eval("a = fn a, b -> a + b end; a . (1, 2)").
function_call_without_assigning_with_spaces_test() ->
{3, _} = eval("(fn a, b -> a + b end) . (1, 2)").
function_call_with_assignment_and_spaces_test() ->
{3, [{a, _}, {c, 3}]} = eval("a = fn x -> x + 2 end; c = a . (1)").
function_call_with_multiple_spaces_test() ->
{3, _} = eval("a = fn a, b -> a + b end; a . (1, 2)").
function_call_with_multiline_test() ->
{3, _} = eval("a = fn a, b -> a + b end; a . \n (1, 2)").
function_call_with_tabs_test() ->
{3, _} = eval("a = fn a, b -> a + b end; a .\n\t(1, 2)").
function_call_with_args_and_nested_when_test() ->
{Fun, _} = eval("fn a, b when a == 1 when b == 2 -> a + b end"),
3 = Fun(1, 2),
2 = Fun(0, 2),
1 = Fun(1, 0),
?assertError(function_clause, Fun(0, 0)).
function_call_with_parens_args_and_nested_when_test() ->
{Fun, _} = eval("fn\n(a, b) when a == 1 when b == 2 -> a + b\nend"),
3 = Fun(1, 2),
2 = Fun(0, 2),
1 = Fun(1, 0),
?assertError(function_clause, Fun(0, 0)).
%% Partial application
require_partial_application_test() ->
{Fun, _} = eval("&List.flatten(&1)"),
Fun = fun 'Elixir.List':flatten/1.
import_partial_application_test() ->
{Fun, _} = eval("&is_atom(&1)"),
Fun = fun erlang:is_atom/1.
|