diff options
Diffstat (limited to 'lib/stdlib/src/erl_pp.erl')
-rw-r--r-- | lib/stdlib/src/erl_pp.erl | 84 |
1 files changed, 81 insertions, 3 deletions
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 74b42a3b40..191aa75698 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -24,7 +24,8 @@ -export([form/1,form/2, attribute/1,attribute/2,function/1,function/2, - guard/1,guard/2,exprs/1,exprs/2,exprs/3,expr/1,expr/2,expr/3,expr/4]). + guard/1,guard/2,exprs/1,exprs/2,exprs/3,expr/1,expr/2,expr/3,expr/4, + legalize_vars/1]). -import(lists, [append/1,foldr/3,map/2,mapfoldl/3,reverse/1,reverse/2]). -import(io_lib, [write/1,format/2]). @@ -199,6 +200,34 @@ expr(E, I, P, Options) -> ?TEST(E), frmt(lexpr(E, P, options(Options)), I, state(Options)). +-spec(legalize_vars(Function) -> erl_parse:abstract_form() when + Function :: erl_parse:abstract_form()). + +legalize_vars({function,ANNO,Name0,Arity,Clauses0}) -> + ?TEST(F), + %% Collect all used variables in this function and classify them + %% as either syntactically valid or not. + F = fun({var,_Anno,Name}, {Valid, Invalid}) -> + Str = [First|_] = atom_to_list(Name), + case First of + X when X >= $a, X =< $z -> + {Valid,Invalid#{Name => Str}}; + _ -> + {Valid#{Name => Name},Invalid} + end + end, + {Valid, Invalid} = fold_vars(F, {#{}, #{}}, Clauses0), + %% Make up an unique variable name for each key in Invalid, then + %% replace all invalid names. + Mapping = maps:fold(fun legalize_name/3, Valid, Invalid), + Subs = fun({var,Anno,Name}) -> + {var,Anno,map_get(Name, Mapping)} + end, + Clauses = map_vars(Subs, Clauses0), + {function,ANNO,Name0,Arity,Clauses}; +legalize_vars(Form) -> + erlang:error(badarg, [Form]). + %%% %%% Local functions %%% @@ -675,12 +704,26 @@ lexpr({'catch',_,Expr}, Prec, Opts) -> {P,R} = preop_prec('catch'), El = {list,[{step,'catch',lexpr(Expr, R, Opts)}]}, maybe_paren(P, Prec, El); +lexpr({'maybe',_,Es}, _, Opts) -> + {list,[{step,'maybe',body(Es, Opts)},{reserved,'end'}]}; +lexpr({'maybe',_,Es,{'else',_,Cs}}, _, Opts) -> + {list,[{step,'maybe',body(Es, Opts)},{step,'else',cr_clauses(Cs, Opts)},{reserved,'end'}]}; +lexpr({maybe_match,_,Lhs,Rhs}, _, Opts) -> + Pl = lexpr(Lhs, 0, Opts), + Rl = lexpr(Rhs, 0, Opts), + {list,[{cstep,[Pl,leaf(" ?=")],Rl}]}; lexpr({match,_,Lhs,Rhs}, Prec, Opts) -> {L,P,R} = inop_prec('='), Pl = lexpr(Lhs, L, Opts), Rl = lexpr(Rhs, R, Opts), El = {list,[{cstep,[Pl,' ='],Rl}]}, maybe_paren(P, Prec, El); +lexpr({op,_,Op,Arg}, Prec, Opts) when Op =:= '+'; + Op =:= '-' -> + {P,R} = preop_prec(Op), + Ol = {reserved, leaf(atom_to_list(Op))}, + El = [Ol,lexpr(Arg, R, Opts)], + maybe_paren(P, Prec, El); lexpr({op,_,Op,Arg}, Prec, Opts) -> {P,R} = preop_prec(Op), Ol = {reserved, leaf(format("~s ", [Op]))}, @@ -1324,7 +1367,7 @@ wordtable() -> L = [begin {leaf,Sz,S} = leaf(W), {S,Sz} end || W <- [" ->"," =","<<",">>","[]","after","begin","case","catch", "end","fun","if","of","receive","try","when"," ::","..", - " |"]], + " |","maybe","else"]], list_to_tuple(L). word(' ->', WT) -> element(1, WT); @@ -1345,4 +1388,39 @@ word('try', WT) -> element(15, WT); word('when', WT) -> element(16, WT); word(' ::', WT) -> element(17, WT); word('..', WT) -> element(18, WT); -word(' |', WT) -> element(19, WT). +word(' |', WT) -> element(19, WT); +word('maybe', WT) -> element(20, WT); +word('else', WT) -> element(21, WT). + +%% Make up an unique variable name for Name that won't clash with any +%% name in Used. We first try by converting the name to uppercase and +%% if that fails we start prepending 'X'es until we find an unused +%% name. +legalize_name(InvalidName, StringName, Used) -> + Upper = string:to_upper(StringName), + NewName = list_to_atom(Upper), + case Used of + #{ NewName := _ } -> + legalize_name(InvalidName, [$X|StringName], Used); + #{} -> + Used#{ InvalidName => NewName } + end. + +fold_vars(F, Acc0, Forms) when is_list(Forms) -> + lists:foldl(fun(Elem, Acc) -> fold_vars(F, Acc, Elem) end, Acc0, Forms); +fold_vars(F, Acc0, V={var,_,_}) -> + F(V, Acc0); +fold_vars(F, Acc0, Form) when is_tuple(Form) -> + lists:foldl(fun(Elem, Acc) -> fold_vars(F, Acc, Elem) end, + Acc0, tuple_to_list(Form)); +fold_vars(_, Acc, _) -> + Acc. + +map_vars(F, Forms) when is_list(Forms) -> + [map_vars(F, Form) || Form <- Forms]; +map_vars(F, V={var,_,_}) -> + F(V); +map_vars(F, Form) when is_tuple(Form) -> + list_to_tuple([map_vars(F, Elem) || Elem <- tuple_to_list(Form)]); +map_vars(_, Form) -> + Form. |