Permalink
Browse files

Keep variables in a single dictionary

This ensures we give the same treatment to quoted
and non-quoted variables.
  • Loading branch information...
1 parent 9b3d851 commit b32f6ef51ff54bed9918ead27ca1de2041b22257 @josevalim josevalim committed Dec 2, 2012
@@ -16,10 +16,8 @@
name_args=false, %% when true, it means arguments should be named
module=nil, %% the current module
function=nil, %% the current function
- recur=nil, %% the current loop function to be recurred
vars=[], %% a dict of defined variables and their alias
temp_vars=[], %% a dict of all variables defined in a particular assign
- quote_vars=[], %% a dict of all quoted variables
clause_vars=nil, %% a dict of all variables defined in a particular clause
extra_guards=nil, %% extra guards from args expansion
counter=[], %% a counter for the variables defined
@@ -117,7 +117,6 @@ eval_forms(Tree, Binding, RawScope) ->
Scope = RawScope#elixir_scope{
vars=binding_dict(Binding),
temp_vars=[],
- quote_vars=[],
clause_vars=nil,
counter=[]
},
@@ -135,7 +134,8 @@ to_binary(Bin) when is_binary(Bin) -> Bin;
to_binary(List) when is_list(List) -> list_to_binary(List).
binding_dict(List) -> binding_dict(List, orddict:new()).
-binding_dict([{H,_}|T], Dict) -> binding_dict(T, orddict:store(H, H, Dict));
+binding_dict([{{H,Kind},_}|T], Dict) -> binding_dict(T, orddict:store({ H, Kind }, H, Dict));
+binding_dict([{H,_}|T], Dict) -> binding_dict(T, orddict:store({ H, nil }, H, Dict));
binding_dict([], Dict) -> Dict.
final_binding(Binding, Vars) -> final_binding(Binding, [], Binding, Vars).
@@ -144,7 +144,7 @@ final_binding([{Var,_}|T], Acc, Binding, Vars) ->
true ->
final_binding(T, Acc, Binding, Vars);
false ->
- RealName = orddict:fetch(Var, Vars),
+ RealName = orddict:fetch({ Var, nil }, Vars),
RealValue = proplists:get_value(RealName, Binding, nil),
final_binding(T, [{Var, RealValue}|Acc], Binding, Vars)
end;
@@ -131,7 +131,8 @@ do_match(Line, DecoupledClauses, S) ->
end.
expand_clauses(Line, [Clause|T], [ClauseVars|V], LeftVars, FinalVars, Acc, S) ->
- RightVars = [normalize_clause_var(Var, Kind, OldValue, ClauseVars) || { Var, Kind, _, OldValue } <- FinalVars],
+ RightVars = [normalize_clause_var(Var, Kind, OldValue, ClauseVars) ||
+ { Var, Kind, _, OldValue } <- FinalVars],
AssignExpr = generate_match(Line, LeftVars, RightVars),
ClauseExprs = element(5, Clause),
@@ -202,29 +203,23 @@ has_match_tuple(_) -> false.
% Normalize the given var checking its existence in the scope var dictionary.
-normalize_vars({ Var, quoted }, S) ->
- normalize_vars(Var, quoted, #elixir_scope.quote_vars, S);
-
-normalize_vars({ Var, Kind }, S) ->
- normalize_vars(Var, Kind, #elixir_scope.vars, S).
-
-normalize_vars(Var, Kind, Index, #elixir_scope{clause_vars=ClauseVars} = S) ->
- Vars = element(Index, S),
-
+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,
- S2 = setelement(Index, S1, orddict:store(Var, NewValue, Vars)),
- S3 = S2#elixir_scope{clause_vars=orddict:store({ Var, Kind }, NewValue, ClauseVars)},
+ S2 = S1#elixir_scope{
+ vars=orddict:store(Key, NewValue, Vars),
+ clause_vars=orddict:store(Key, NewValue, ClauseVars)
+ },
- Expr = case orddict:find(Var, Vars) of
+ Expr = case orddict:find(Key, Vars) of
{ ok, OldValue } -> { var, 0, OldValue };
error -> { atom, 0, nil }
end,
- { { Var, Kind, NewValue, Expr }, S3 }.
+ { { Var, Kind, NewValue, Expr }, S2 }.
% Normalize a var by checking if it was defined in the clause.
% If so, use it, otherwise use from main scope.
@@ -62,7 +62,7 @@ eval_forms(Forms, Line, Value, Vars, S) ->
end.
eval_compilation(Forms, Vars, S) ->
- Binding = [{ Var, Value } || { _, Var, Value } <- Vars],
+ Binding = [{ { Var, Kind }, Value } || { _, Kind, Var, Value } <- Vars],
{ Result, _Binding, FS } = elixir:eval_forms(Forms, [{'_@MODULE',nil}|Binding], S),
{ Result, FS }.
@@ -71,7 +71,7 @@ code_loading_compilation(Forms, Line, Value, Vars, S) ->
{ Exprs, FS } = elixir_translator:translate(Forms, S),
ModuleForm = module_form(Exprs, Line, S#elixir_scope.file, Module, Vars),
- Args = [X || { _, _, X } <- Vars],
+ Args = [X || { _, _, _, X } <- Vars],
%% Pass { native, false } to speed up bootstrap
%% process when native is set to true
@@ -127,7 +127,7 @@ no_auto_import() ->
module_form(Exprs, Line, File, Module, Vars) when
is_binary(File), is_list(Exprs), is_integer(Line), is_atom(Module) ->
- Cons = lists:foldr(fun({ _, Var, _ }, Acc) ->
+ Cons = lists:foldr(fun({ _, _, Var, _ }, Acc) ->
{ cons, Line, { var, Line, Var }, Acc }
end, { nil, Line }, Vars),
@@ -43,15 +43,16 @@ translate(Line, Ref, Block, S) ->
MetaBlock = elixir_tree_helpers:abstract_syntax(Block),
MetaS = elixir_scope:serialize(S),
- Vars = orddict:fold(fun(Key, Value, { Acc, Counter }) ->
+ { Vars, _ } = orddict:fold(fun({ Key, Kind }, Value, { Acc, Counter }) ->
{ { cons, Line, { tuple, Line, [
{ atom, Line, Key },
+ { atom, Line, Kind },
{ atom, Line, ?ELIXIR_ATOM_CONCAT(["_@", Counter]) },
{ var, Line, Value }
] }, Acc }, Counter + 1 }
end, { { nil, Line }, 0 }, S#elixir_scope.vars),
- Args = [{integer, Line, Line}, Ref, MetaBlock, element(1, Vars), MetaS],
+ Args = [{integer, Line, Line}, Ref, MetaBlock, Vars, MetaS],
?ELIXIR_WRAP_CALL(Line, ?MODULE, compile, Args).
%% The compilation hook.
@@ -92,7 +93,7 @@ compile(Line, Other, _Block, _Vars, #elixir_scope{file=File}) ->
elixir_errors:form_error(Line, File, ?MODULE, { invalid_module, Other });
compile(Line, Module, Block, Vars, RawS) ->
- Dict = [{ X, Y } || { X, Y, _ } <- Vars],
+ Dict = [{ { Name, Kind }, Value } || { Name, Kind, Value, _ } <- Vars],
S = elixir_scope:deserialize(RawS, Dict),
compile(Line, Module, Block, Vars, S).
@@ -1,4 +1,3 @@
-
%% Convenience functions used to manipulate scope
%% and its variables.
-module(elixir_scope).
@@ -23,20 +22,20 @@ translate_var(Line, Name, Kind, S) ->
case S#elixir_scope.context of
assign ->
TempVars = S#elixir_scope.temp_vars,
- case { orddict:is_key(Name, Vars), orddict:find(Name, TempVars) } of
+ case { orddict:is_key({ Name, Kind }, Vars), orddict:find(Name, TempVars) } of
{ true, { ok, Kind } } ->
- { {var, Line, orddict:fetch(Name, Vars) }, S };
+ { { var, Line, orddict:fetch({ Name, Kind }, Vars) }, S };
{ Else, _ } ->
{ NewVar, NS } = if
Kind == quoted -> build_erl_var(Line, S);
Else -> build_erl_var(Line, Name, S);
S#elixir_scope.noname -> build_erl_var(Line, Name, S);
- true -> { {var, Line, Name}, S }
+ true -> { { var, Line, Name }, S }
end,
RealName = element(3, NewVar),
ClauseVars = S#elixir_scope.clause_vars,
{ NewVar, NS#elixir_scope{
- vars=orddict:store(Name, RealName, Vars),
+ vars=orddict:store({ Name, Kind }, RealName, Vars),
temp_vars=orddict:store(Name, Kind, TempVars),
clause_vars=if
ClauseVars == nil -> nil;
@@ -45,9 +44,9 @@ translate_var(Line, Name, Kind, S) ->
} }
end;
_ ->
- case orddict:is_key(Name, Vars) of
- false -> elixir_translator:translate_each({Name, Line, []}, S);
- true -> { {var, Line, orddict:fetch(Name, Vars) }, S }
+ case orddict:find({ Name, Kind }, Vars) of
+ { ok, VarName } -> { { var, Line, VarName }, S };
+ error -> elixir_translator:translate_each({ Name, Line, [] }, S)
end
end
end.
@@ -121,13 +120,10 @@ deserialize({ File, Functions, CheckClauses, Requires, Macros, Aliases, Schedule
umergev(S1, S2) ->
V1 = S1#elixir_scope.vars,
V2 = S2#elixir_scope.vars,
- Q1 = S1#elixir_scope.quote_vars,
- Q2 = S2#elixir_scope.quote_vars,
C1 = S1#elixir_scope.clause_vars,
C2 = S2#elixir_scope.clause_vars,
S2#elixir_scope{
vars=orddict:merge(fun var_merger/3, V1, V2),
- quote_vars=orddict:merge(fun var_merger/3, Q1, Q2),
clause_vars=merge_clause_vars(C1, C2)
}.
@@ -148,13 +144,10 @@ umergec(S1, S2) ->
merge_clause_vars(nil, _C2) -> nil;
merge_clause_vars(_C1, nil) -> nil;
merge_clause_vars(C1, C2) ->
- orddict:merge(fun clause_var_merger/3, C1, C2).
-
-clause_var_merger({ Var, _ }, K1, K2) ->
- var_merger(Var, K1, K2).
+ orddict:merge(fun var_merger/3, C1, C2).
-var_merger(Var, Var, K2) -> K2;
-var_merger(Var, K1, Var) -> K1;
+var_merger({ Var, _ }, Var, K2) -> K2;
+var_merger({ Var, _ }, K1, Var) -> K1;
var_merger(_Var, K1, K2) ->
V1 = var_number(atom_to_list(K1), []),
V2 = var_number(atom_to_list(K2), []),
@@ -77,9 +77,9 @@ translate_each({ '__op__', Line, [Op, Left, Right] }, S) when is_atom(Op) ->
{ { op, Line, convert_op(Op), TLeft, TRight }, NS };
translate_each({ '__ambiguousop__', Line, [Var, H|T] }, S) ->
- { Name, _, _ } = Var,
+ { Name, _, Kind } = Var,
- case orddict:find(Name, S#elixir_scope.vars) of
+ case orddict:find({ Name, Kind }, S#elixir_scope.vars) of
error -> translate_each({ Name, Line, [H|T] }, S);
_ ->
case T of
@@ -103,7 +103,7 @@ translate_each({ alias, Line, [Ref, KV] }, S) ->
{ atom, _, Old } ->
{ New, SF } = case lists:keyfind(as, 1, KV) of
Opt when Opt == { as, true }; Opt == false ->
- { elixir_aliases:last(Old), SR };
+ { elixir_aliases:last(Old), SR };
{ as, false } ->
{ Old, SR };
{ as, Other } ->
@@ -284,7 +284,7 @@ translate_each({ quote, GivenLine, [T] }, S) when is_list(T) ->
translate_each({ quote, GivenLine, [_] }, S) ->
syntax_error(GivenLine, S#elixir_scope.file, "invalid args for quote");
-
+
%% Functions
translate_each({ fn, Line, [[{do, { '->', _, Pairs }}]] }, S) ->
@@ -330,41 +330,23 @@ translate_each({ 'super?', Line, [] }, S) ->
%% Variables
-translate_each({ '^', Line, [ { Name, _, Args } ] }, S) ->
- Dict = case Args of
- nil -> S#elixir_scope.vars;
- quoted -> S#elixir_scope.quote_vars;
- _ ->
- syntax_error(Line, S#elixir_scope.file, "cannot use ^ with expression at ^~s, ^ must be used only with variables", [Name])
- end,
-
- Result = case S#elixir_scope.context of
- assign ->
- case orddict:find(Name, Dict) of
- error -> "unbound variable ^~s";
- { ok, Value } -> { {var, Line, Value}, S }
- end;
- _ -> "cannot access variable ^~s outside of assignment"
- end,
+translate_each({ '^', Line, [ { Name, _, Kind } ] }, S) when is_list(Kind) ->
+ syntax_error(Line, S#elixir_scope.file, "cannot use ^ with expression at ^~s, ^ must be used only with variables", [Name]);
- case is_list(Result) of
- true ->
- syntax_error(Line, S#elixir_scope.file, Result, [Name]);
- false ->
- Result
+translate_each({ '^', Line, [ { Name, _, Kind } ] }, #elixir_scope{context=assign} = S) when is_atom(Kind) ->
+ case orddict:find({ Name, Kind }, S#elixir_scope.vars) of
+ { ok, Value } ->
+ { { var, Line, Value }, S };
+ error ->
+ syntax_error(Line, S#elixir_scope.file, "unbound variable ^~s", [Name])
end;
-translate_each({ Name, Line, quoted }, S) when is_atom(Name) ->
- NewS = S#elixir_scope{vars=S#elixir_scope.quote_vars,noname=true},
- { TVar, VS } = elixir_scope:translate_var(Line, Name, quoted, NewS),
- { TVar, VS#elixir_scope{
- quote_vars=VS#elixir_scope.vars,
- noname=S#elixir_scope.noname,
- vars=S#elixir_scope.vars
- } };
-
-translate_each({ Name, Line, nil }, S) when is_atom(Name) ->
- elixir_scope:translate_var(Line, Name, nil, S);
+translate_each({ '^', Line, [ { Name, _, Kind } ] }, S) when is_atom(Kind) ->
+ syntax_error(Line, S#elixir_scope.file,
+ "cannot access variable ^~s outside of assignment", [Name]);
+
+translate_each({ Name, Line, Kind }, S) when is_atom(Name), (Kind == nil orelse Kind == quoted) ->
+ elixir_scope:translate_var(Line, Name, Kind, S);
%% Local calls

0 comments on commit b32f6ef

Please sign in to comment.