Skip to content

Commit c572cb8

Browse files
author
José Valim
committed
Support binding/2 that goes across all contexts
1 parent b761f1a commit c572cb8

File tree

5 files changed

+71
-9
lines changed

5 files changed

+71
-9
lines changed

lib/elixir/lib/file.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ end
5656

5757
defmodule File do
5858
@moduledoc """
59-
This module contains function to manipulate files.
59+
This module contains functions to manipulate files.
6060
6161
Some of those functions are low-level, allowing the user
6262
to interact with the file or IO devices, like `File.open/2`,

lib/elixir/lib/kernel.ex

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2467,6 +2467,26 @@ defmodule Kernel do
24672467
"""
24682468
defmacro binding(list)
24692469

2470+
@doc """
2471+
Receives a list of tuples at compilation time containing the
2472+
variable name and its scope and returns the binding of the given
2473+
variables as a keyword list with the variable name and scope pair
2474+
as key and the variable value as value.
2475+
2476+
In case a variable in the list does not exist in the binding,
2477+
it is not included in the returned result.
2478+
2479+
## Examples
2480+
2481+
iex> var!(x, :foo) = 1
2482+
iex> binding([x: nil], true)
2483+
[]
2484+
iex> binding([x: :foo], true)
2485+
[{ { :x, :foo }, 1 }]
2486+
2487+
"""
2488+
defmacro binding(list, true)
2489+
24702490
@doc """
24712491
Provides an `if` macro. This macro expects the first argument to
24722492
be a condition and the rest are keyword arguments.

lib/elixir/src/elixir_dispatch.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ in_erlang_macros() ->
449449
{apply,3},
450450
{binding,0},
451451
{binding,1},
452+
{binding,2},
452453
{'case',2},
453454
{def,1},
454455
{def,2},

lib/elixir/src/elixir_macros.erl

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,17 @@ translate({'@', Meta, [{ Name, _, Args }]}, S) ->
133133
%% Binding
134134

135135
translate({ 'binding', Meta, [] }, S) ->
136+
translate({ 'binding', Meta, [false] }, S);
137+
138+
translate({ 'binding', Meta, [false] }, S) ->
136139
Line = ?line(Meta),
137140
{ elixir_tree_helpers:list_to_cons(Line,
138-
[ { tuple, Line, [
139-
{ atom, Line, Name },
140-
{ var, Line, Var }
141-
] } || { { Name, nil }, Var } <- S#elixir_scope.vars]), S };
141+
[ to_var_value_tuple(Line, Name, Var) || { { Name, nil }, Var } <- S#elixir_scope.vars]), S };
142+
143+
translate({ 'binding', Meta, [true] }, S) ->
144+
Line = ?line(Meta),
145+
{ elixir_tree_helpers:list_to_cons(Line,
146+
[ to_var_value_tuple(Line, Name, Kind, Var) || { { Name, Kind }, Var } <- S#elixir_scope.vars]), S };
142147

143148
translate({ 'binding', Meta, [List] }, #elixir_scope{vars=Vars} = S) when is_list(List) ->
144149
Line = ?line(Meta),
@@ -155,10 +160,24 @@ translate({ 'binding', Meta, [List] }, #elixir_scope{vars=Vars} = S) when is_lis
155160
"at compilation time, got: ~ts", ['Elixir.Macro':to_string(Name)])
156161
end, [], List),
157162
{ elixir_tree_helpers:list_to_cons(Line,
158-
[ { tuple, Line, [
159-
{ atom, Line, Name },
160-
{ var, Line, Var }
161-
] } || { Name, Var } <- Dict]), S };
163+
[ to_var_value_tuple(Line, Name, Var) || { Name, Var } <- Dict]), S };
164+
165+
translate({ 'binding', Meta, [List, true] }, #elixir_scope{vars=Vars} = S) when is_list(List) ->
166+
Line = ?line(Meta),
167+
Dict = lists:foldl(fun
168+
(Tuple, Acc) when is_tuple(Tuple) ->
169+
case orddict:find(Tuple, Vars) of
170+
{ ok, Var } ->
171+
orddict:store(Tuple, Var, Acc);
172+
error ->
173+
Acc
174+
end;
175+
(Tuple, _Acc) ->
176+
elixir_errors:syntax_error(Line, S#elixir_scope.file, "binding/2 expects a list of tuples "
177+
"at compilation time, got: ~ts", ['Elixir.Macro':to_string(Tuple)])
178+
end, [], List),
179+
{ elixir_tree_helpers:list_to_cons(Line,
180+
[ to_var_value_tuple(Line, Name, Kind, Var) || { { Name, Kind }, Var } <- Dict]), S };
162181

163182
%% Case
164183

@@ -382,6 +401,19 @@ is_reserved_data(moduledoc) -> true;
382401
is_reserved_data(doc) -> true;
383402
is_reserved_data(_) -> false.
384403

404+
to_var_value_tuple(Line, Name, Var) ->
405+
{ tuple, Line,
406+
[ { atom, Line, Name },
407+
{ var, Line, Var } ] }.
408+
409+
to_var_value_tuple(Line, Name, Kind, Var) ->
410+
{ tuple, Line,
411+
[ { tuple, Line, [
412+
{ atom, Line, Name },
413+
{ atom, Line, Kind }
414+
] },
415+
{ var, Line, Var } ] }.
416+
385417
spec_to_macro(type) -> deftype;
386418
spec_to_macro(typep) -> deftypep;
387419
spec_to_macro(opaque) -> defopaque;

lib/elixir/test/elixir/kernel_test.exs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ defmodule KernelTest do
9494
assert binding == [x: 2, y: 3]
9595
end
9696

97+
test :binding_with_all_contexts do
98+
x = 1
99+
assert { { :x, nil }, 1 } in binding(true)
100+
101+
var!(x, :foo) = 2
102+
assert { { :x, :foo }, 2 } in binding(true)
103+
assert binding([x: :foo], true) == [{ { :x, :foo }, 2 }]
104+
end
105+
97106
defp x(value) when value in [1, 2, 3], do: true
98107
defp x(_), do: false
99108

0 commit comments

Comments
 (0)