Expand fn

This commit is contained in:
José Valim
2013-12-23 18:23:08 +01:00
parent 51aef55c83
commit f81a15b7cb
4 changed files with 56 additions and 11 deletions
+5 -2
View File
@@ -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) ->
+20
View File
@@ -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).
+11 -3
View File
@@ -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