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
This commit is contained in:
@@ -2173,8 +2173,8 @@ defmodule Kernel do
|
||||
"This will"
|
||||
end
|
||||
"""
|
||||
defmacro cond([do: { :->, _, pairs }]) do
|
||||
[{ [condition], meta, clause }|t] = :lists.reverse pairs
|
||||
defmacro cond([do: pairs]) do
|
||||
[{ :->, meta, [[condition], clause] }|t] = :lists.reverse pairs
|
||||
|
||||
new_acc =
|
||||
case condition do
|
||||
@@ -2205,22 +2205,20 @@ defmodule Kernel do
|
||||
# end
|
||||
# end
|
||||
#
|
||||
defp build_cond_clauses([{ [condition], new, clause }|t], acc, old) do
|
||||
stab = { :->, [], [falsy_clause(old, acc), truthy_clause(new, clause)] }
|
||||
acc = quote do
|
||||
case unquote(condition), do: unquote(stab)
|
||||
end
|
||||
defp build_cond_clauses([{ :->, new, [[condition], clause] }|t], acc, old) do
|
||||
clauses = [falsy_clause(old, acc), truthy_clause(new, clause)]
|
||||
acc = quote do: (case unquote(condition), do: unquote(clauses))
|
||||
build_cond_clauses(t, acc, new)
|
||||
end
|
||||
|
||||
defp build_cond_clauses([], acc, _), do: acc
|
||||
|
||||
defp falsy_clause(meta, acc) do
|
||||
{ [quote(do: unquote(cond_var) when unquote(cond_var) in [false, nil])], meta, acc }
|
||||
{ :->, meta, [[quote(do: unquote(cond_var) when unquote(cond_var) in [false, nil])], acc] }
|
||||
end
|
||||
|
||||
defp truthy_clause(meta, clause) do
|
||||
{ [quote(do: _)], meta, clause }
|
||||
{ :->, meta, [[quote(do: _)], clause] }
|
||||
end
|
||||
|
||||
# Setting cond: true in metadata turns on a small optimization
|
||||
|
||||
@@ -552,11 +552,11 @@ defmodule Kernel.Typespec do
|
||||
|
||||
defp typespec_to_ast({ :type, line, :fun, [{:type, _, :product, args}, result] }) do
|
||||
args = lc arg inlist args, do: typespec_to_ast(arg)
|
||||
{ :->, [line: line], [{args, [line: line], typespec_to_ast(result)}] }
|
||||
[{ :->, [line: line], [args, typespec_to_ast(result)] }]
|
||||
end
|
||||
|
||||
defp typespec_to_ast({ :type, line, :fun, [args, result] }) do
|
||||
{ :->, [line: line], [{[typespec_to_ast(args)], [line: line], typespec_to_ast(result)}] }
|
||||
[{ :->, [line: line], [[typespec_to_ast(args)], typespec_to_ast(result)] }]
|
||||
end
|
||||
|
||||
defp typespec_to_ast({ :type, line, :fun, [] }) do
|
||||
@@ -669,11 +669,7 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
# Handle funs
|
||||
defp typespec({:->, meta, [{[{:fun, _, arguments}], cmeta, return}]}, vars, caller) when is_list(arguments) do
|
||||
typespec({:->, meta, [{arguments, cmeta, return}]}, vars, caller)
|
||||
end
|
||||
|
||||
defp typespec({:->, meta, [{arguments, _, return}]}, vars, caller) when is_list(arguments) do
|
||||
defp typespec([{:->, meta, [arguments, return]}], vars, caller) when is_list(arguments) do
|
||||
args = fn_args(meta, arguments, return, vars, caller)
|
||||
{ :type, line(meta), :fun, args }
|
||||
end
|
||||
|
||||
+16
-11
@@ -5,8 +5,11 @@ defmodule Macro do
|
||||
Conveniences for working with macros.
|
||||
"""
|
||||
|
||||
@typedoc "Abstract Syntax Tree (AST) node"
|
||||
@type t :: { t, t } | { t, Keyword.t, t } | atom | number | binary | list
|
||||
@typedoc "Abstract Syntax Tree (AST)"
|
||||
@type t :: node | { t, t } | atom | number | binary | [t]
|
||||
|
||||
@typedoc "The AST node (remaining ones are literals)"
|
||||
@type node :: { node | atom, Keyword.t, atom | [t] }
|
||||
|
||||
@binary_ops [ :===, :!==,
|
||||
:==, :!=, :<=, :>=,
|
||||
@@ -309,22 +312,22 @@ defmodule Macro do
|
||||
end
|
||||
|
||||
# Fn keyword
|
||||
def to_string({ :fn, _, [{ :->, _, [{_, _, tuple}] } = arrow] } = ast, fun)
|
||||
def to_string({ :fn, _, [{ :->, _, [_, tuple] }] = arrow } = ast, fun)
|
||||
when not is_tuple(tuple) or elem(tuple, 0) != :__block__ do
|
||||
fun.(ast, "fn " <> arrow_to_string(arrow, fun) <> " end")
|
||||
end
|
||||
|
||||
def to_string({ :fn, _, [{ :->, _, [_] } = block] } = ast, fun) do
|
||||
def to_string({ :fn, _, [{ :->, _, _ }] = block } = ast, fun) do
|
||||
fun.(ast, "fn " <> block_to_string(block, fun) <> "\nend")
|
||||
end
|
||||
|
||||
def to_string({ :fn, _, [block] } = ast, fun) do
|
||||
def to_string({ :fn, _, block } = ast, fun) do
|
||||
block = adjust_new_lines block_to_string(block, fun), "\n "
|
||||
fun.(ast, "fn\n " <> block <> "\nend")
|
||||
end
|
||||
|
||||
# left -> right
|
||||
def to_string({ :->, _, _ } = ast, fun) do
|
||||
def to_string([{ :->, _, _ }|_] = ast, fun) do
|
||||
fun.(ast, "(" <> arrow_to_string(ast, fun, true) <> ")")
|
||||
end
|
||||
|
||||
@@ -442,8 +445,8 @@ defmodule Macro do
|
||||
atom_to_binary(key) <> "\n " <> block <> "\n"
|
||||
end
|
||||
|
||||
defp block_to_string({ :->, _, exprs }, fun) do
|
||||
Enum.map_join(exprs, "\n", fn({ left, _, right }) ->
|
||||
defp block_to_string([{ :->, _, _ }|_] = block, fun) do
|
||||
Enum.map_join(block, "\n", fn({ :->, _, [left, right] }) ->
|
||||
left = comma_join_or_empty_paren(left, fun, false)
|
||||
left <> "->\n " <> adjust_new_lines block_to_string(right, fun), "\n "
|
||||
end)
|
||||
@@ -483,8 +486,8 @@ defmodule Macro do
|
||||
|
||||
defp op_to_string(expr, fun, _, _), do: to_string(expr, fun)
|
||||
|
||||
defp arrow_to_string({ :->, _, pairs }, fun, paren // false) do
|
||||
Enum.map_join(pairs, "; ", fn({ left, _, right }) ->
|
||||
defp arrow_to_string(pairs, fun, paren // false) do
|
||||
Enum.map_join(pairs, "; ", fn({ :->, _, [left, right] }) ->
|
||||
left = comma_join_or_empty_paren(left, fun, paren)
|
||||
left <> "-> " <> to_string(right, fun)
|
||||
end)
|
||||
@@ -707,7 +710,9 @@ defmodule Macro do
|
||||
{ tree, cache }
|
||||
end
|
||||
|
||||
@doc false # Used internally by Elixir
|
||||
# TODO: Rewrite this function to use elixir_exp
|
||||
# For such, there is no need for a cache.
|
||||
@doc false
|
||||
def expand_all(tree, env, cache) do
|
||||
expand_all_until(expand(tree, env, cache), env)
|
||||
end
|
||||
|
||||
@@ -13,16 +13,24 @@ get_pairs(Meta, Key, Clauses, S) ->
|
||||
|
||||
get_pairs(Meta, Key, Clauses, S, AllowNil) ->
|
||||
case lists:keyfind(Key, 1, Clauses) of
|
||||
{ Key, { '->', _, Pairs } } ->
|
||||
[{ Key, PMeta, Left, Right } || { Left, PMeta, Right } <- Pairs];
|
||||
{ Key, Pairs } when is_list(Pairs) ->
|
||||
[get_pair(Meta, Key, Pair, S) || Pair <- Pairs];
|
||||
{ Key, nil } when AllowNil ->
|
||||
[];
|
||||
{ Key, _ } ->
|
||||
elixir_errors:syntax_error(Meta, S#elixir_scope.file, "expected pairs with -> for key ~ts", [Key]);
|
||||
get_pairs_error(Meta, Key, S);
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
get_pair(_Meta, Key, { '->', Meta, [Left, Right] }, _S) ->
|
||||
{ Key, Meta, Left, Right };
|
||||
get_pair(Meta, Key, _Other, S) ->
|
||||
get_pairs_error(Meta, Key, S).
|
||||
|
||||
get_pairs_error(Meta, Key, S) ->
|
||||
elixir_errors:compile_error(Meta, S#elixir_scope.file, "expected -> clauses for key ~ts", [Key]).
|
||||
|
||||
% Function for translating assigns.
|
||||
|
||||
assigns(Fun, Args, #elixir_scope{context=Context, temp_vars=TempVars,
|
||||
|
||||
@@ -203,6 +203,10 @@ expand({ quote, Meta, [_, _] }, E) ->
|
||||
|
||||
%% Functions
|
||||
|
||||
%% TODO: Remove me. Temporary during refactoring.
|
||||
expand({ '&', _, [Arg] } = Original, E) when is_integer(Arg) ->
|
||||
{ Original, E };
|
||||
|
||||
expand({ '&', Meta, [Arg] }, E) ->
|
||||
% assert_no_match_or_guard_scope(Meta, '&', S),
|
||||
case elixir_fn:capture(Meta, Arg, E) of
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
-include("elixir.hrl").
|
||||
|
||||
fn(Meta, Clauses, S) ->
|
||||
Transformer = fun({ ArgsWithGuards, CMeta, Expr }, Acc) ->
|
||||
Transformer = fun({ '->', CMeta, [ArgsWithGuards, Expr] }, Acc) ->
|
||||
{ Args, Guards } = elixir_clauses:extract_splat_guards(ArgsWithGuards),
|
||||
elixir_clauses:assigns_block(?line(CMeta), fun translate_fn_match/2, Args, [Expr], Guards, umergec(S, Acc))
|
||||
end,
|
||||
@@ -25,6 +25,8 @@ translate_fn_match(Arg, S) ->
|
||||
{ TArg, TS } = elixir_translator:translate(Arg, S#elixir_scope{extra=fn_match}),
|
||||
{ TArg, TS#elixir_scope{extra=S#elixir_scope.extra} }.
|
||||
|
||||
%% Capture
|
||||
|
||||
capture(Meta, { '/', _, [{ { '.', _, [_, F] } = Dot, RequireMeta , [] }, A] }, E) when is_atom(F), is_integer(A) ->
|
||||
Args = [{ '&', [], [X] } || X <- lists:seq(1, A)],
|
||||
capture_require(Meta, { Dot, RequireMeta, Args }, E, true);
|
||||
@@ -97,7 +99,7 @@ do_capture(Meta, Expr, E, Sequential) ->
|
||||
invalid_capture(Meta, Expr, E);
|
||||
{ EExpr, EDict } ->
|
||||
EVars = validate(Meta, EDict, 1, E),
|
||||
Fn = { fn, Meta, [{ '->', [], [{ EVars, Meta, EExpr }]}]},
|
||||
Fn = { fn, Meta, [{ '->', Meta, [EVars, EExpr]}]},
|
||||
{ expanded, Fn, E#elixir_env{macro_counter=E#elixir_env.macro_counter+1} }
|
||||
end.
|
||||
|
||||
|
||||
@@ -578,7 +578,7 @@ extract_identifier(Other) -> Other.
|
||||
%% Fn
|
||||
|
||||
build_fn(Op, Stab) ->
|
||||
{ fn, meta(Op), [Stab] }.
|
||||
{ fn, meta(Op), Stab }.
|
||||
|
||||
%% Access
|
||||
|
||||
@@ -632,20 +632,20 @@ string_tokens_parse(Line, Tokens) ->
|
||||
%% Keywords
|
||||
|
||||
build_stab([{ '->', Meta, [Left, Right] }|T]) ->
|
||||
{ '->', Meta, build_stab(Meta, T, Left, [Right], []) };
|
||||
build_stab(Meta, T, Left, [Right], []);
|
||||
|
||||
build_stab(Else) ->
|
||||
build_block(Else).
|
||||
|
||||
build_stab(Old, [{ '->', New, [Left, Right] }|T], Marker, Temp, Acc) ->
|
||||
H = { Marker, Old, build_block(lists:reverse(Temp)) },
|
||||
H = { '->', Old, [Marker, build_block(lists:reverse(Temp))] },
|
||||
build_stab(New, T, Left, [Right], [H|Acc]);
|
||||
|
||||
build_stab(Meta, [H|T], Marker, Temp, Acc) ->
|
||||
build_stab(Meta, T, Marker, [H|Temp], Acc);
|
||||
|
||||
build_stab(Meta, [], Marker, Temp, Acc) ->
|
||||
H = { Marker, Meta, build_block(lists:reverse(Temp)) },
|
||||
H = { '->', Meta, [Marker, build_block(lists:reverse(Temp))] },
|
||||
lists:reverse([H|Acc]).
|
||||
|
||||
%% Every time the parser sees a (unquote_splicing())
|
||||
|
||||
@@ -243,9 +243,9 @@ translate_each({ '&', Meta, [Arg] }, S) ->
|
||||
translate_each(TE, S#elixir_scope{macro_counter=MacroCounter})
|
||||
end;
|
||||
|
||||
translate_each({ fn, Meta, [{ '->', _, Pairs }] }, S) ->
|
||||
translate_each({ fn, Meta, Clauses }, S) ->
|
||||
assert_no_match_or_guard_scope(Meta, 'fn', S),
|
||||
elixir_fn:fn(Meta, Pairs, S);
|
||||
elixir_fn:fn(Meta, Clauses, S);
|
||||
|
||||
%% Case
|
||||
|
||||
|
||||
@@ -112,22 +112,22 @@ defmodule Kernel.QuoteTest do
|
||||
end
|
||||
|
||||
test :when do
|
||||
assert {:->,_,[{[{:when,_,[1,2,3,4]}],_,5}]} = quote(do: (1, 2, 3 when 4 -> 5))
|
||||
assert {:->,_,[{[{:when,_,[1,2,3,4]}],_,5}]} = quote(do: ((1, 2, 3) when 4 -> 5))
|
||||
assert [{:->,_,[[{:when,_,[1,2,3,4]}],5]}] = quote(do: (1, 2, 3 when 4 -> 5))
|
||||
assert [{:->,_,[[{:when,_,[1,2,3,4]}],5]}] = quote(do: ((1, 2, 3) when 4 -> 5))
|
||||
|
||||
assert {:->,_,[{[{:when,_,[1,2,3,{:when,_,[4,5]}]}],_,6}]} =
|
||||
assert [{:->,_,[[{:when,_,[1,2,3,{:when,_,[4,5]}]}],6]}] =
|
||||
quote(do: ((1, 2, 3) when 4 when 5 -> 6))
|
||||
end
|
||||
|
||||
test :stab do
|
||||
assert { :->, _, [{[], _, _}] } = (quote do -> end)
|
||||
assert { :->, _, [{[], _, _}] } = (quote do: (->))
|
||||
assert [{ :->, _, [[], nil] }] = (quote do -> end)
|
||||
assert [{ :->, _, [[], nil] }] = (quote do: (->))
|
||||
|
||||
assert { :->, _, [{[1], _, _}] } = (quote do 1 -> end)
|
||||
assert { :->, _, [{[1], _, _}] } = (quote do: (1 ->))
|
||||
assert [{ :->, _, [[1], nil] }] = (quote do 1 -> end)
|
||||
assert [{ :->, _, [[1], nil] }] = (quote do: (1 ->))
|
||||
|
||||
assert { :->, _, [{[], _, 1}] } = (quote do -> 1 end)
|
||||
assert { :->, _, [{[], _, 1}] } = (quote do: (-> 1))
|
||||
assert [{ :->, _, [[], 1] }] = (quote do -> 1 end)
|
||||
assert [{ :->, _, [[], 1] }] = (quote do: (-> 1))
|
||||
end
|
||||
|
||||
test :bind_quoted do
|
||||
|
||||
Reference in New Issue
Block a user