Skip to content

Commit 51aef55

Browse files
author
José Valim
committed
Change how -> is represented in the AST
The previous representation was very hard to compose and very often required the AST nodes to be created manually. The current approach is easier to compose because the node is represented as any other operator as long as it is wrapped in parentheses. For example, imagine we have pairs of patterns and the code to be executed for that pattern, and we want to inject them into a case. It can now be writen as: pairs = Enum.map pairs, fn { pattern, expr } -> quote do: (unquote(pattern) -> unquote(expr)) end quote do case unquote(condition), do: unquote(pairs) end Compare to the previous implementation: pairs = Enum.map pairs, fn { pattern, expr } -> { [pattern], [], expr } end pairs = { :->, [], pairs } quote do case unquote(condition), do: unquote(pairs) end
1 parent 2d36a76 commit 51aef55

File tree

9 files changed

+60
-47
lines changed

9 files changed

+60
-47
lines changed

lib/elixir/lib/kernel.ex

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2173,8 +2173,8 @@ defmodule Kernel do
21732173
"This will"
21742174
end
21752175
"""
2176-
defmacro cond([do: { :->, _, pairs }]) do
2177-
[{ [condition], meta, clause }|t] = :lists.reverse pairs
2176+
defmacro cond([do: pairs]) do
2177+
[{ :->, meta, [[condition], clause] }|t] = :lists.reverse pairs
21782178

21792179
new_acc =
21802180
case condition do
@@ -2205,22 +2205,20 @@ defmodule Kernel do
22052205
# end
22062206
# end
22072207
#
2208-
defp build_cond_clauses([{ [condition], new, clause }|t], acc, old) do
2209-
stab = { :->, [], [falsy_clause(old, acc), truthy_clause(new, clause)] }
2210-
acc = quote do
2211-
case unquote(condition), do: unquote(stab)
2212-
end
2208+
defp build_cond_clauses([{ :->, new, [[condition], clause] }|t], acc, old) do
2209+
clauses = [falsy_clause(old, acc), truthy_clause(new, clause)]
2210+
acc = quote do: (case unquote(condition), do: unquote(clauses))
22132211
build_cond_clauses(t, acc, new)
22142212
end
22152213

22162214
defp build_cond_clauses([], acc, _), do: acc
22172215

22182216
defp falsy_clause(meta, acc) do
2219-
{ [quote(do: unquote(cond_var) when unquote(cond_var) in [false, nil])], meta, acc }
2217+
{ :->, meta, [[quote(do: unquote(cond_var) when unquote(cond_var) in [false, nil])], acc] }
22202218
end
22212219

22222220
defp truthy_clause(meta, clause) do
2223-
{ [quote(do: _)], meta, clause }
2221+
{ :->, meta, [[quote(do: _)], clause] }
22242222
end
22252223

22262224
# Setting cond: true in metadata turns on a small optimization

lib/elixir/lib/kernel/typespec.ex

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -552,11 +552,11 @@ defmodule Kernel.Typespec do
552552

553553
defp typespec_to_ast({ :type, line, :fun, [{:type, _, :product, args}, result] }) do
554554
args = lc arg inlist args, do: typespec_to_ast(arg)
555-
{ :->, [line: line], [{args, [line: line], typespec_to_ast(result)}] }
555+
[{ :->, [line: line], [args, typespec_to_ast(result)] }]
556556
end
557557

558558
defp typespec_to_ast({ :type, line, :fun, [args, result] }) do
559-
{ :->, [line: line], [{[typespec_to_ast(args)], [line: line], typespec_to_ast(result)}] }
559+
[{ :->, [line: line], [[typespec_to_ast(args)], typespec_to_ast(result)] }]
560560
end
561561

562562
defp typespec_to_ast({ :type, line, :fun, [] }) do
@@ -669,11 +669,7 @@ defmodule Kernel.Typespec do
669669
end
670670

671671
# Handle funs
672-
defp typespec({:->, meta, [{[{:fun, _, arguments}], cmeta, return}]}, vars, caller) when is_list(arguments) do
673-
typespec({:->, meta, [{arguments, cmeta, return}]}, vars, caller)
674-
end
675-
676-
defp typespec({:->, meta, [{arguments, _, return}]}, vars, caller) when is_list(arguments) do
672+
defp typespec([{:->, meta, [arguments, return]}], vars, caller) when is_list(arguments) do
677673
args = fn_args(meta, arguments, return, vars, caller)
678674
{ :type, line(meta), :fun, args }
679675
end

lib/elixir/lib/macro.ex

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ defmodule Macro do
55
Conveniences for working with macros.
66
"""
77

8-
@typedoc "Abstract Syntax Tree (AST) node"
9-
@type t :: { t, t } | { t, Keyword.t, t } | atom | number | binary | list
8+
@typedoc "Abstract Syntax Tree (AST)"
9+
@type t :: node | { t, t } | atom | number | binary | [t]
10+
11+
@typedoc "The AST node (remaining ones are literals)"
12+
@type node :: { node | atom, Keyword.t, atom | [t] }
1013

1114
@binary_ops [ :===, :!==,
1215
:==, :!=, :<=, :>=,
@@ -309,22 +312,22 @@ defmodule Macro do
309312
end
310313

311314
# Fn keyword
312-
def to_string({ :fn, _, [{ :->, _, [{_, _, tuple}] } = arrow] } = ast, fun)
315+
def to_string({ :fn, _, [{ :->, _, [_, tuple] }] = arrow } = ast, fun)
313316
when not is_tuple(tuple) or elem(tuple, 0) != :__block__ do
314317
fun.(ast, "fn " <> arrow_to_string(arrow, fun) <> " end")
315318
end
316319

317-
def to_string({ :fn, _, [{ :->, _, [_] } = block] } = ast, fun) do
320+
def to_string({ :fn, _, [{ :->, _, _ }] = block } = ast, fun) do
318321
fun.(ast, "fn " <> block_to_string(block, fun) <> "\nend")
319322
end
320323

321-
def to_string({ :fn, _, [block] } = ast, fun) do
324+
def to_string({ :fn, _, block } = ast, fun) do
322325
block = adjust_new_lines block_to_string(block, fun), "\n "
323326
fun.(ast, "fn\n " <> block <> "\nend")
324327
end
325328

326329
# left -> right
327-
def to_string({ :->, _, _ } = ast, fun) do
330+
def to_string([{ :->, _, _ }|_] = ast, fun) do
328331
fun.(ast, "(" <> arrow_to_string(ast, fun, true) <> ")")
329332
end
330333

@@ -442,8 +445,8 @@ defmodule Macro do
442445
atom_to_binary(key) <> "\n " <> block <> "\n"
443446
end
444447

445-
defp block_to_string({ :->, _, exprs }, fun) do
446-
Enum.map_join(exprs, "\n", fn({ left, _, right }) ->
448+
defp block_to_string([{ :->, _, _ }|_] = block, fun) do
449+
Enum.map_join(block, "\n", fn({ :->, _, [left, right] }) ->
447450
left = comma_join_or_empty_paren(left, fun, false)
448451
left <> "->\n " <> adjust_new_lines block_to_string(right, fun), "\n "
449452
end)
@@ -483,8 +486,8 @@ defmodule Macro do
483486

484487
defp op_to_string(expr, fun, _, _), do: to_string(expr, fun)
485488

486-
defp arrow_to_string({ :->, _, pairs }, fun, paren // false) do
487-
Enum.map_join(pairs, "; ", fn({ left, _, right }) ->
489+
defp arrow_to_string(pairs, fun, paren // false) do
490+
Enum.map_join(pairs, "; ", fn({ :->, _, [left, right] }) ->
488491
left = comma_join_or_empty_paren(left, fun, paren)
489492
left <> "-> " <> to_string(right, fun)
490493
end)
@@ -707,7 +710,9 @@ defmodule Macro do
707710
{ tree, cache }
708711
end
709712

710-
@doc false # Used internally by Elixir
713+
# TODO: Rewrite this function to use elixir_exp
714+
# For such, there is no need for a cache.
715+
@doc false
711716
def expand_all(tree, env, cache) do
712717
expand_all_until(expand(tree, env, cache), env)
713718
end

lib/elixir/src/elixir_clauses.erl

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,24 @@ get_pairs(Meta, Key, Clauses, S) ->
1313

1414
get_pairs(Meta, Key, Clauses, S, AllowNil) ->
1515
case lists:keyfind(Key, 1, Clauses) of
16-
{ Key, { '->', _, Pairs } } ->
17-
[{ Key, PMeta, Left, Right } || { Left, PMeta, Right } <- Pairs];
16+
{ Key, Pairs } when is_list(Pairs) ->
17+
[get_pair(Meta, Key, Pair, S) || Pair <- Pairs];
1818
{ Key, nil } when AllowNil ->
1919
[];
2020
{ Key, _ } ->
21-
elixir_errors:syntax_error(Meta, S#elixir_scope.file, "expected pairs with -> for key ~ts", [Key]);
21+
get_pairs_error(Meta, Key, S);
2222
_ ->
2323
[]
2424
end.
2525

26+
get_pair(_Meta, Key, { '->', Meta, [Left, Right] }, _S) ->
27+
{ Key, Meta, Left, Right };
28+
get_pair(Meta, Key, _Other, S) ->
29+
get_pairs_error(Meta, Key, S).
30+
31+
get_pairs_error(Meta, Key, S) ->
32+
elixir_errors:compile_error(Meta, S#elixir_scope.file, "expected -> clauses for key ~ts", [Key]).
33+
2634
% Function for translating assigns.
2735

2836
assigns(Fun, Args, #elixir_scope{context=Context, temp_vars=TempVars,

lib/elixir/src/elixir_exp.erl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ expand({ quote, Meta, [_, _] }, E) ->
203203

204204
%% Functions
205205

206+
%% TODO: Remove me. Temporary during refactoring.
207+
expand({ '&', _, [Arg] } = Original, E) when is_integer(Arg) ->
208+
{ Original, E };
209+
206210
expand({ '&', Meta, [Arg] }, E) ->
207211
% assert_no_match_or_guard_scope(Meta, '&', S),
208212
case elixir_fn:capture(Meta, Arg, E) of

lib/elixir/src/elixir_fn.erl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
-include("elixir.hrl").
66

77
fn(Meta, Clauses, S) ->
8-
Transformer = fun({ ArgsWithGuards, CMeta, Expr }, Acc) ->
8+
Transformer = fun({ '->', CMeta, [ArgsWithGuards, Expr] }, Acc) ->
99
{ Args, Guards } = elixir_clauses:extract_splat_guards(ArgsWithGuards),
1010
elixir_clauses:assigns_block(?line(CMeta), fun translate_fn_match/2, Args, [Expr], Guards, umergec(S, Acc))
1111
end,
@@ -25,6 +25,8 @@ translate_fn_match(Arg, S) ->
2525
{ TArg, TS } = elixir_translator:translate(Arg, S#elixir_scope{extra=fn_match}),
2626
{ TArg, TS#elixir_scope{extra=S#elixir_scope.extra} }.
2727

28+
%% Capture
29+
2830
capture(Meta, { '/', _, [{ { '.', _, [_, F] } = Dot, RequireMeta , [] }, A] }, E) when is_atom(F), is_integer(A) ->
2931
Args = [{ '&', [], [X] } || X <- lists:seq(1, A)],
3032
capture_require(Meta, { Dot, RequireMeta, Args }, E, true);
@@ -97,7 +99,7 @@ do_capture(Meta, Expr, E, Sequential) ->
9799
invalid_capture(Meta, Expr, E);
98100
{ EExpr, EDict } ->
99101
EVars = validate(Meta, EDict, 1, E),
100-
Fn = { fn, Meta, [{ '->', [], [{ EVars, Meta, EExpr }]}]},
102+
Fn = { fn, Meta, [{ '->', Meta, [EVars, EExpr]}]},
101103
{ expanded, Fn, E#elixir_env{macro_counter=E#elixir_env.macro_counter+1} }
102104
end.
103105

lib/elixir/src/elixir_parser.yrl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ extract_identifier(Other) -> Other.
578578
%% Fn
579579

580580
build_fn(Op, Stab) ->
581-
{ fn, meta(Op), [Stab] }.
581+
{ fn, meta(Op), Stab }.
582582

583583
%% Access
584584

@@ -632,20 +632,20 @@ string_tokens_parse(Line, Tokens) ->
632632
%% Keywords
633633

634634
build_stab([{ '->', Meta, [Left, Right] }|T]) ->
635-
{ '->', Meta, build_stab(Meta, T, Left, [Right], []) };
635+
build_stab(Meta, T, Left, [Right], []);
636636

637637
build_stab(Else) ->
638638
build_block(Else).
639639

640640
build_stab(Old, [{ '->', New, [Left, Right] }|T], Marker, Temp, Acc) ->
641-
H = { Marker, Old, build_block(lists:reverse(Temp)) },
641+
H = { '->', Old, [Marker, build_block(lists:reverse(Temp))] },
642642
build_stab(New, T, Left, [Right], [H|Acc]);
643643

644644
build_stab(Meta, [H|T], Marker, Temp, Acc) ->
645645
build_stab(Meta, T, Marker, [H|Temp], Acc);
646646

647647
build_stab(Meta, [], Marker, Temp, Acc) ->
648-
H = { Marker, Meta, build_block(lists:reverse(Temp)) },
648+
H = { '->', Meta, [Marker, build_block(lists:reverse(Temp))] },
649649
lists:reverse([H|Acc]).
650650

651651
%% Every time the parser sees a (unquote_splicing())

lib/elixir/src/elixir_translator.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,9 @@ translate_each({ '&', Meta, [Arg] }, S) ->
243243
translate_each(TE, S#elixir_scope{macro_counter=MacroCounter})
244244
end;
245245

246-
translate_each({ fn, Meta, [{ '->', _, Pairs }] }, S) ->
246+
translate_each({ fn, Meta, Clauses }, S) ->
247247
assert_no_match_or_guard_scope(Meta, 'fn', S),
248-
elixir_fn:fn(Meta, Pairs, S);
248+
elixir_fn:fn(Meta, Clauses, S);
249249

250250
%% Case
251251

lib/elixir/test/elixir/kernel/quote_test.exs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,22 +112,22 @@ defmodule Kernel.QuoteTest do
112112
end
113113

114114
test :when do
115-
assert {:->,_,[{[{:when,_,[1,2,3,4]}],_,5}]} = quote(do: (1, 2, 3 when 4 -> 5))
116-
assert {:->,_,[{[{:when,_,[1,2,3,4]}],_,5}]} = quote(do: ((1, 2, 3) when 4 -> 5))
115+
assert [{:->,_,[[{:when,_,[1,2,3,4]}],5]}] = quote(do: (1, 2, 3 when 4 -> 5))
116+
assert [{:->,_,[[{:when,_,[1,2,3,4]}],5]}] = quote(do: ((1, 2, 3) when 4 -> 5))
117117

118-
assert {:->,_,[{[{:when,_,[1,2,3,{:when,_,[4,5]}]}],_,6}]} =
118+
assert [{:->,_,[[{:when,_,[1,2,3,{:when,_,[4,5]}]}],6]}] =
119119
quote(do: ((1, 2, 3) when 4 when 5 -> 6))
120120
end
121121

122122
test :stab do
123-
assert { :->, _, [{[], _, _}] } = (quote do -> end)
124-
assert { :->, _, [{[], _, _}] } = (quote do: (->))
123+
assert [{ :->, _, [[], nil] }] = (quote do -> end)
124+
assert [{ :->, _, [[], nil] }] = (quote do: (->))
125125

126-
assert { :->, _, [{[1], _, _}] } = (quote do 1 -> end)
127-
assert { :->, _, [{[1], _, _}] } = (quote do: (1 ->))
126+
assert [{ :->, _, [[1], nil] }] = (quote do 1 -> end)
127+
assert [{ :->, _, [[1], nil] }] = (quote do: (1 ->))
128128

129-
assert { :->, _, [{[], _, 1}] } = (quote do -> 1 end)
130-
assert { :->, _, [{[], _, 1}] } = (quote do: (-> 1))
129+
assert [{ :->, _, [[], 1] }] = (quote do -> 1 end)
130+
assert [{ :->, _, [[], 1] }] = (quote do: (-> 1))
131131
end
132132

133133
test :bind_quoted do

0 commit comments

Comments
 (0)