diff options
author | José Valim <jose.valim@plataformatec.com.br> | 2013-12-23 18:23:08 +0100 |
---|---|---|
committer | José Valim <jose.valim@plataformatec.com.br> | 2013-12-23 18:23:08 +0100 |
commit | f81a15b7cb1dbba4d7ef4203fdd4e3ed3a9ddd7d (patch) | |
tree | 70a42b472518c90faaa01899977ec0a50849f1cf /lib/elixir | |
parent | 51aef55c830b770d85c1add1eccd6dcb440fd018 (diff) | |
download | elixir-f81a15b7cb1dbba4d7ef4203fdd4e3ed3a9ddd7d.tar.gz |
Expand fn
Diffstat (limited to 'lib/elixir')
-rw-r--r-- | lib/elixir/src/elixir_exp.erl | 7 | ||||
-rw-r--r-- | lib/elixir/src/elixir_exp_clauses.erl | 20 | ||||
-rw-r--r-- | lib/elixir/src/elixir_fn.erl | 14 | ||||
-rw-r--r-- | lib/elixir/test/elixir/kernel/expansion_test.exs | 26 |
4 files changed, 56 insertions, 11 deletions
diff --git a/lib/elixir/src/elixir_exp.erl b/lib/elixir/src/elixir_exp.erl index 770d523cb..5d174ed62 100644 --- a/lib/elixir/src/elixir_exp.erl +++ b/lib/elixir/src/elixir_exp.erl @@ -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) -> diff --git a/lib/elixir/src/elixir_exp_clauses.erl b/lib/elixir/src/elixir_exp_clauses.erl index e8ef50774..d923782be 100644 --- a/lib/elixir/src/elixir_exp_clauses.erl +++ b/lib/elixir/src/elixir_exp_clauses.erl @@ -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). diff --git a/lib/elixir/src/elixir_fn.erl b/lib/elixir/src/elixir_fn.erl index f48642a89..20b6cc320 100644 --- a/lib/elixir/src/elixir_fn.erl +++ b/lib/elixir/src/elixir_fn.erl @@ -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), diff --git a/lib/elixir/test/elixir/kernel/expansion_test.exs b/lib/elixir/test/elixir/kernel/expansion_test.exs index 3c0547a9d..c0c4a18de 100644 --- a/lib/elixir/test/elixir/kernel/expansion_test.exs +++ b/lib/elixir/test/elixir/kernel/expansion_test.exs @@ -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 |