Permalink
Browse files

Record rewriting works with case clauses

  • Loading branch information...
1 parent d4c2436 commit 498ac849fba0421ecb09448e105739d73151c7b0 @josevalim josevalim committed Dec 2, 2012
@@ -121,6 +121,28 @@ defmodule Kernel.RecordRewriter do
end
end
+ defp optimize_expr({ :case, line, expr, clauses }, dict) do
+ { expr, dict, _ } = optimize_expr(expr, dict)
+ tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
+ clauses = lc { clause, _, _ } inlist tuples, do: clause
+ dict = join_dict(tuples)
+ res = join_result(tuples)
+ { { :case, line, expr, clauses }, dict, res }
+ end
+
+ defp optimize_expr({ :fun, line, { :function, module, name, arity } }, dict) do
+ { module, dict, _ } = optimize_expr(module, dict)
+ { name, dict, _ } = optimize_expr(name, dict)
+ { arity, dict, _ } = optimize_expr(arity, dict)
+ { { :fun, line, { :function, module, name, arity } }, dict, nil }
+ end
+
+ defp optimize_expr({ :fun, line, { :clauses, clauses } }, dict) do
+ tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
+ clauses = lc { clause, _, _ } inlist tuples, do: clause
+ { { :fun, line, { :clauses, clauses } }, dict, nil }
+ end
+
defp optimize_expr({ comprehension, line, expr, args }, dict) when comprehension in [:lc, :bc] do
{ args, new_dict } = optimize_args(args, dict)
{ expr, _, _ } = optimize_expr(expr, new_dict)
@@ -133,11 +155,11 @@ defmodule Kernel.RecordRewriter do
{ { generate, line, left, right }, dict, nil }
end
- defp optimize_expr(other, dict) when elem(other, 0) in [:string, :atom, :integer, :float, :nil] do
+ defp optimize_expr(other, dict) when elem(other, 0) in [:string, :atom, :integer, :float, :nil, :fun] do
{ other, dict, nil }
end
- ## Match related
+ ## Helpers
defp optimize_tuple_args(args, dict) do
{ final_args, { final_dict, final_acc } } =
@@ -204,6 +226,43 @@ defmodule Kernel.RecordRewriter do
vars
end
+ defp join_dict([{ _, dict, _ }|t]) do
+ join_dict(t, dict)
+ end
+
+ defp join_dict([{ _, dict, _ }|t], other) do
+ other = Enum.reduce other, other, fn
+ { key, value }, acc ->
+ case :orddict.find(key, dict) do
+ { :ok, ^value } -> acc
+ { :ok, _ } -> :orddict.store(key, nil, acc)
+ :error -> :orddict.erase(key, acc)
+ end
+ end
+
+ join_dict(t, other)
+ end
+
+ defp join_dict([], other) do
+ other
+ end
+
+ defp join_result([{ _, _, res }|t]) do
+ join_result(t, res)
+ end
+
+ defp join_result([{ _, _, res }|t], res) do
+ join_result(t, res)
+ end
+
+ defp join_result([{ _, _, _ }|_], _res) do
+ nil
+ end
+
+ defp join_result([], res) do
+ res
+ end
+
## Record helpers
# TODO: Implement proper record check
@@ -110,17 +110,19 @@ do_match(Line, DecoupledClauses, S) ->
% Now get all the variables defined inside each clause
CV = lists:reverse(RawCV),
- NewVars = lists:umerge([lists:sort(orddict:fetch_keys(X)) || X <- CV]),
- case NewVars of
+ AllVars = lists:umerge([orddict:fetch_keys(X) || X <- CV]),
+ SharedVars = ordsets:intersection(CV),
+
+ case AllVars of
[] -> { TClauses, TS };
_ ->
% Create a new scope that contains a list of all variables
% defined inside all the clauses. It returns this new scope and
% a list of tuples where the first element is the variable name,
% the second one is the new pointer to the variable and the third
% is the old pointer.
- { FinalVars, FS } = lists:mapfoldl(fun normalize_vars/2, TS, NewVars),
+ { FinalVars, FS } = lists:mapfoldl(fun(X, Acc) -> normalize_vars(X, SharedVars, Acc) end, TS, AllVars),
% Defines a tuple that will be used as left side of the match operator
LeftVars = [{var, Line, NewValue} || { _, _, NewValue, _ } <- FinalVars],
@@ -204,11 +206,18 @@ has_match_tuple(_) -> false.
% Normalize the given var checking its existence in the scope var dictionary.
-normalize_vars({ Var, Kind } = Key, #elixir_scope{vars=Vars,clause_vars=ClauseVars} = S) ->
- { { _, _, NewValue }, S1 } = if
- (Kind == quoted) or (S#elixir_scope.noname) -> elixir_scope:build_erl_var(0, S);
- true -> elixir_scope:build_erl_var(0, Var, "_@" ++ atom_to_list(Var), S)
- end,
+normalize_vars({ Var, Kind } = Key, Shared, #elixir_scope{vars=Vars,clause_vars=ClauseVars} = S) ->
+ { NewValue, S1 } =
+ case orddict:find(Key, Shared) of
+ { ok, SharedValue } ->
+ { SharedValue, S };
+ error ->
+ { { _, _, ErlValue }, ErlS } = case (Kind == quoted) or (S#elixir_scope.noname) of
+ true -> elixir_scope:build_erl_var(0, S);
+ false -> elixir_scope:build_erl_var(0, Var, "_@" ++ atom_to_list(Var), S)
+ end,
+ { ErlValue, ErlS }
+ end,
S2 = S1#elixir_scope{
vars=orddict:store(Key, NewValue, Vars),
@@ -25,33 +25,33 @@ defmodule Kernel.RecordRewriterTest do
end
test "with left-side arg match" do
- clause = clause(fn(arg = Macro.Env[]) -> :foo end)
- assert optimize_clause(clause) == { clause, [arg: Macro.Env], nil }
+ clause = clause(fn(x = Macro.Env[]) -> :foo end)
+ assert optimize_clause(clause) == { clause, [x: Macro.Env], nil }
end
test "with right-side arg match" do
- clause = clause(fn(Macro.Env[] = arg) -> :foo end)
- assert optimize_clause(clause) == { clause, [arg: Macro.Env], nil }
+ clause = clause(fn(Macro.Env[] = x) -> :foo end)
+ assert optimize_clause(clause) == { clause, [x: Macro.Env], nil }
end
test "with nested left-side arg match" do
- clause = clause(fn(arg = other = Macro.Env[]) -> :foo end)
- assert optimize_clause(clause) == { clause, [arg: Macro.Env, other: Macro.Env], nil }
+ clause = clause(fn(x = y = Macro.Env[]) -> :foo end)
+ assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Macro.Env], nil }
end
test "with nested right-side arg match" do
- clause = clause(fn(Macro.Env[] = arg = other) -> :foo end)
- assert optimize_clause(clause) == { clause, [arg: Macro.Env, other: Macro.Env], nil }
+ clause = clause(fn(Macro.Env[] = x = y) -> :foo end)
+ assert optimize_clause(clause) == { clause, [x: Macro.Env, y: Macro.Env], nil }
end
test "with deep left-side arg match" do
- clause = clause(fn({ x, arg = Macro.Env[] }) -> :foo end)
- assert optimize_clause(clause) == { clause, [arg: Macro.Env], nil }
+ clause = clause(fn({ x, y = Macro.Env[] }) -> :foo end)
+ assert optimize_clause(clause) == { clause, [y: Macro.Env], nil }
end
test "with deep right-side arg match" do
- clause = clause(fn({ x, Macro.Env[] = arg }) -> :foo end)
- assert optimize_clause(clause) == { clause, [arg: Macro.Env], nil }
+ clause = clause(fn({ x, Macro.Env[] = y }) -> :foo end)
+ assert optimize_clause(clause) == { clause, [y: Macro.Env], nil }
end
test "with tuple match" do
@@ -121,4 +121,31 @@ defmodule Kernel.RecordRewriterTest do
clause = clause(fn -> bc x = Macro.Env[] inbits sample, do: <<x>> end)
assert optimize_clause(clause) == { clause, [], nil }
end
+
+ test "inside function retrieval" do
+ clause = clause(fn -> function(x = Macro.Env[], y, z) end)
+ assert optimize_clause(clause) == { clause, [x: Macro.Env], nil }
+ end
+
+ test "inside anonymous function" do
+ clause = clause(fn -> fn (x = Macro.Env[]) -> x end end)
+ assert optimize_clause(clause) == { clause, [], nil }
+ end
+
+ test "inside case" do
+ clause = clause(fn -> case x = Macro.Env[] do _ -> x end end)
+ assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
+
+ clause = clause(fn -> case something do x = Macro.Env[] -> x; Macro.Env[] = x -> x end end)
+ assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
+
+ clause = clause(fn -> case something do x = Macro.Env[] -> x; Macro.Env[] = y -> y end end)
+ assert optimize_clause(clause) == { clause, [], { Macro.Env, nil } }
+
+ clause = clause(fn -> case something do 1 -> x = Macro.Env[]; 2 -> x = Macro.Env[] end end)
+ assert optimize_clause(clause) == { clause, [x: Macro.Env], { Macro.Env, nil } }
+
+ clause = clause(fn -> case something do 1 -> x = Macro.Env[]; 2 -> x = Range[] end end)
+ assert optimize_clause(clause) == { clause, [x: nil], nil }
+ end
end

0 comments on commit 498ac84

Please sign in to comment.