Expand fn
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
-module(elixir_exp).
|
||||
-export([expand/2]).
|
||||
-export([expand/2, expand_many/2]).
|
||||
-import(elixir_errors, [compile_error/3, compile_error/4]).
|
||||
-include("elixir.hrl").
|
||||
|
||||
@@ -206,7 +206,6 @@ expand({ quote, Meta, [_, _] }, E) ->
|
||||
%% 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
|
||||
@@ -216,6 +215,10 @@ expand({ '&', Meta, [Arg] }, E) ->
|
||||
expand(Expr, EE)
|
||||
end;
|
||||
|
||||
expand({ fn, Meta, Pairs }, E) ->
|
||||
% assert_no_match_or_guard_scope(Meta, 'fn', S),
|
||||
elixir_fn:expand(Meta, Pairs, E);
|
||||
|
||||
%% Comprehensions
|
||||
|
||||
expand({ Kind, Meta, Args }, E) when is_list(Args), (Kind == lc) orelse (Kind == bc) ->
|
||||
|
||||
@@ -2,9 +2,29 @@
|
||||
%% fn, receive and friends. try is handled in elixir_try.
|
||||
-module(elixir_exp_clauses).
|
||||
-export([match/3]).
|
||||
-export([expand_clause/3]).
|
||||
-include("elixir.hrl").
|
||||
|
||||
match(Fun, Expr, #elixir_env{context=Context} = E) ->
|
||||
{ EExpr, EE } = Fun(Expr, E#elixir_env{context=match}),
|
||||
{ EExpr, EE#elixir_env{context=Context} }.
|
||||
|
||||
expand_clause(Fun, { '->', Meta, [Left, Right] }, E) ->
|
||||
{ ELeft, EL } = expand_head(Fun, Left, E),
|
||||
{ ERight, ER } = elixir_exp:expand(Right, EL),
|
||||
{ { '->', Meta, [ELeft, ERight] }, ER }.
|
||||
|
||||
expand_head(Fun, [{ 'when', Meta, [_,_|_] = All }], E) ->
|
||||
{ Args, Guard } = elixir_utils:split_last(All),
|
||||
{ EArgs, EA } = match(Fun, Args, E),
|
||||
{ EGuard, EG } = expand_guard(Guard, EA#elixir_env{context=guard}),
|
||||
{ [{ 'when', Meta, EArgs ++ [EGuard] }], EG#elixir_env{context=E#elixir_env.context} };
|
||||
expand_head(Fun, Args, E) ->
|
||||
match(Fun, Args, E).
|
||||
|
||||
expand_guard({ 'when', Meta, [Left, Right] }, E) ->
|
||||
{ ELeft, EL } = expand_guard(Left, E),
|
||||
{ ERight, ER } = expand_guard(Right, EL),
|
||||
{ { 'when', Meta, [ELeft, ERight] }, ER };
|
||||
expand_guard(Other, E) ->
|
||||
elixir_exp:expand(Other, E).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-module(elixir_fn).
|
||||
-export([fn/3, capture/3]).
|
||||
-export([fn/3, capture/3, expand/3]).
|
||||
-import(elixir_scope, [umergec/2]).
|
||||
-import(elixir_errors, [syntax_error/3, compile_error/4]).
|
||||
-include("elixir.hrl").
|
||||
@@ -25,6 +25,16 @@ 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} }.
|
||||
|
||||
%% Expansion
|
||||
|
||||
expand(Meta, Clauses, E) ->
|
||||
Transformer = fun(Clause, Acc) ->
|
||||
{ EClause, EC } = elixir_exp_clauses:expand_clause(fun elixir_exp:expand_many/2, Clause, Acc),
|
||||
{ EClause, elixir_env:mergec(E, EC) }
|
||||
end,
|
||||
{ EClauses, _ } = lists:mapfoldl(Transformer, E, Clauses),
|
||||
{ { fn, Meta, EClauses }, E }.
|
||||
|
||||
%% Capture
|
||||
|
||||
capture(Meta, { '/', _, [{ { '.', _, [_, F] } = Dot, RequireMeta , [] }, A] }, E) when is_atom(F), is_integer(A) ->
|
||||
@@ -72,8 +82,6 @@ capture(Meta, Arg, E) when is_integer(Arg) ->
|
||||
capture(Meta, Arg, E) ->
|
||||
invalid_capture(Meta, Arg, E).
|
||||
|
||||
%% Helpers
|
||||
|
||||
capture_import(Meta, { Atom, ImportMeta, Args } = Expr, E, Sequential) ->
|
||||
Res = Sequential andalso
|
||||
elixir_exp_dispatch:import_function(ImportMeta, Atom, length(Args), E),
|
||||
|
||||
@@ -146,10 +146,6 @@ defmodule Kernel.ExpansionTest do
|
||||
expand(quote do: a =~ b)
|
||||
end
|
||||
|
||||
test "locals: raises on guards" do
|
||||
# TODO
|
||||
end
|
||||
|
||||
test "locals: expands to configured local" do
|
||||
assert expand_env(quote(do: a), __ENV__.local(Hello)) |> elem(0) ==
|
||||
quote(do: :"Elixir.Hello".a())
|
||||
@@ -241,8 +237,26 @@ defmodule Kernel.ExpansionTest do
|
||||
end
|
||||
|
||||
test "&: expands macros" do
|
||||
assert expand(quote do: &Kernel.ExpansionTarget.seventeen/0) ==
|
||||
quote do: fn -> Kernel.ExpansionTarget.seventeen() end
|
||||
|
||||
assert expand(quote do: (require Kernel.ExpansionTarget; &Kernel.ExpansionTarget.seventeen/0)) ==
|
||||
quote do: (require :"Elixir.Kernel.ExpansionTarget", []; fn -> 17 end)
|
||||
end
|
||||
|
||||
## fn
|
||||
|
||||
test "fn: expands each clause" do
|
||||
assert expand(quote do: fn x -> x; _ -> x end) ==
|
||||
quote do: fn x -> x; _ -> x() end
|
||||
end
|
||||
|
||||
test "fn: does not share lexical in between clauses" do
|
||||
assert expand(quote do: fn 1 -> import List; 2 -> flatten([1,2,3]) end) ==
|
||||
quote do: fn 1 -> import :"Elixir.List", []; 2 -> flatten([1,2,3]) end
|
||||
end
|
||||
|
||||
test "fn: expands guards" do
|
||||
assert expand(quote do: fn x when x when __ENV__.context -> true end) ==
|
||||
quote do: fn x when x when :guard -> true end
|
||||
end
|
||||
|
||||
## Invalid
|
||||
|
||||
Reference in New Issue
Block a user