1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1999-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20%% Purpose : Core Erlang (naive) prettyprinter 21 22-module(core_pp). 23 24-export([format/1,format_all/1]). 25 26-include("core_parse.hrl"). 27 28%% ====================================================================== %% 29%% format(Node) -> Text 30%% Node = coreErlang() 31%% Text = string() | [Text] 32%% 33%% Prettyprint-formats (naively) an abstract Core Erlang syntax 34%% tree. 35 36-record(ctxt, {indent = 0 :: integer(), 37 item_indent = 2 :: integer(), 38 body_indent = 4 :: integer(), 39 line = 0 :: integer(), 40 clean = true :: boolean()}). 41 42-define(TAB_WIDTH, 8). 43 44-spec format(cerl:cerl()) -> iolist(). 45 46format(Node) -> 47 format(Node, #ctxt{}). 48 49-spec format_all(cerl:cerl()) -> iolist(). 50 51format_all(Node) -> 52 format(Node, #ctxt{clean=false}). 53 54maybe_anno(Node, Fun, #ctxt{clean=false}=Ctxt) -> 55 As = cerl:get_ann(Node), 56 maybe_anno(Node, Fun, Ctxt, As); 57maybe_anno(Node, Fun, #ctxt{clean=true}=Ctxt) -> 58 As0 = cerl:get_ann(Node), 59 case get_line(As0) of 60 none -> 61 maybe_anno(Node, Fun, Ctxt, As0); 62 Line -> 63 As = strip_line(As0), 64 if Line > Ctxt#ctxt.line -> 65 [io_lib:format("%% Line ~w",[Line]), 66 nl_indent(Ctxt), 67 maybe_anno(Node, Fun, Ctxt#ctxt{line = Line}, As) 68 ]; 69 true -> 70 maybe_anno(Node, Fun, Ctxt, As) 71 end 72 end. 73 74maybe_anno(Node, Fun, Ctxt, []) -> 75 Fun(Node, Ctxt); 76maybe_anno(Node, Fun, Ctxt, List) -> 77 Ctxt1 = add_indent(Ctxt, 2), 78 Ctxt2 = add_indent(Ctxt1, 3), 79 ["( ", 80 Fun(Node, Ctxt1), 81 nl_indent(Ctxt1), 82 "-| ",format_anno(List, Ctxt2)," )" 83 ]. 84 85format_anno([_|_]=List, Ctxt) -> 86 [$[,format_anno_list(List, Ctxt),$]]; 87format_anno({file,Name}, _Ctxt) -> 88 %% Optimization: Reduces file size considerably. 89 io_lib:format("{'file',~p}", [Name]); 90format_anno(Tuple, Ctxt) when is_tuple(Tuple) -> 91 [${,format_anno_list(tuple_to_list(Tuple), Ctxt),$}]; 92format_anno(Val, Ctxt) when is_atom(Val) -> 93 format_1(#c_literal{val=Val}, Ctxt); 94format_anno(Val, Ctxt) when is_integer(Val) -> 95 format_1(#c_literal{val=Val}, Ctxt). 96 97format_anno_list([H|[_|_]=T], Ctxt) -> 98 [format_anno(H, Ctxt), $, | format_anno_list(T, Ctxt)]; 99format_anno_list([H], Ctxt) -> 100 format_anno(H, Ctxt). 101 102strip_line([A | As]) when is_integer(A) -> 103 strip_line(As); 104strip_line([{file,_File} | As]) -> 105 strip_line(As); 106strip_line([A | As]) -> 107 [A | strip_line(As)]; 108strip_line([]) -> 109 []. 110 111get_line([L | _As]) when is_integer(L) -> 112 L; 113get_line([_ | As]) -> 114 get_line(As); 115get_line([]) -> 116 none. 117 118format(Node, Ctxt) -> 119 maybe_anno(Node, fun format_1/2, Ctxt). 120 121format_1(#c_literal{val=[]}, _) -> "[]"; 122format_1(#c_literal{val=I}, _) when is_integer(I) -> integer_to_list(I); 123format_1(#c_literal{val=F}, _) when is_float(F) -> float_to_list(F); 124format_1(#c_literal{val=A}, _) when is_atom(A) -> core_atom(A); 125format_1(#c_literal{val=[H|T]}, Ctxt) -> 126 format_1(#c_cons{hd=#c_literal{val=H},tl=#c_literal{val=T}}, Ctxt); 127format_1(#c_literal{val=Tuple}, Ctxt) when is_tuple(Tuple) -> 128 format_1(#c_tuple{es=[#c_literal{val=E} || E <- tuple_to_list(Tuple)]}, Ctxt); 129format_1(#c_literal{anno=A,val=Bitstring}, Ctxt) when is_bitstring(Bitstring) -> 130 Segs = segs_from_bitstring(Bitstring), 131 format_1(#c_binary{anno=A,segments=Segs}, Ctxt); 132format_1(#c_literal{anno=A,val=M},Ctxt) when is_map(M) -> 133 Pairs = maps:to_list(M), 134 Op = #c_literal{val=assoc}, 135 Cpairs = [#c_map_pair{op=Op, 136 key=#c_literal{val=K}, 137 val=#c_literal{val=V}} || {K,V} <- Pairs], 138 format_1(#c_map{anno=A,arg=#c_literal{val=#{}},es=Cpairs},Ctxt); 139format_1(#c_literal{val=F},_Ctxt) when is_function(F) -> 140 {module,M} = erlang:fun_info(F, module), 141 {name,N} = erlang:fun_info(F, name), 142 {arity,A} = erlang:fun_info(F, arity), 143 ["fun ",core_atom(M),$:,core_atom(N),$/,integer_to_list(A)]; 144format_1(#c_var{name={I,A}}, _) -> 145 [core_atom(I),$/,integer_to_list(A)]; 146format_1(#c_var{name=V}, _) -> 147 %% Internal variable names may be: 148 %% - atoms representing proper Erlang variable names, or 149 %% any atoms that may be printed without single-quoting 150 %% - nonnegative integers. 151 %% It is important that when printing variables, no two names 152 %% should ever map to the same string. 153 if is_atom(V) -> 154 S = atom_to_list(V), 155 case S of 156 [C | _] when C >= $A, C =< $Z -> 157 %% Ordinary uppercase-prefixed names are 158 %% printed just as they are. 159 S; 160 [$_ | _] -> 161 %% Already "_"-prefixed names are prefixed 162 %% with "_X", e.g. '_foo' => '_X_foo', to 163 %% avoid generating things like "____foo" upon 164 %% repeated writing and reading of code. 165 %% ("_X_X_X_foo" is better.) 166 [$_, $X | S]; 167 _ -> 168 %% Plain atoms are prefixed with a single "_". 169 %% E.g. foo => "_foo". 170 [$_ | S] 171 end; 172 is_integer(V) -> 173 %% Integers are also simply prefixed with "_". 174 [$_ | integer_to_list(V)] 175 end; 176format_1(#c_binary{segments=Segs}, Ctxt) -> 177 ["#{", 178 format_vseq(Segs, "", ",", add_indent(Ctxt, 2), 179 fun format_bitstr/2), 180 "}#" 181 ]; 182format_1(#c_tuple{es=Es}, Ctxt) -> 183 [${, 184 format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), 185 $} 186 ]; 187format_1(#c_map{arg=#c_literal{val=M},es=Es}, Ctxt) 188 when is_map(M), map_size(M) =:= 0 -> 189 ["~{", 190 format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), 191 "}~" 192 ]; 193format_1(#c_map{arg=Var,es=Es}, Ctxt) -> 194 ["~{", 195 format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), 196 "|",format(Var, add_indent(Ctxt, 1)), 197 "}~" 198 ]; 199format_1(#c_map_pair{op=#c_literal{val=assoc},key=K,val=V}, Ctxt) -> 200 format_map_pair("=>", K, V, Ctxt); 201format_1(#c_map_pair{op=#c_literal{val=exact},key=K,val=V}, Ctxt) -> 202 format_map_pair(":=", K, V, Ctxt); 203format_1(#c_cons{hd=H,tl=T}, Ctxt) -> 204 Txt = ["["|format(H, add_indent(Ctxt, 1))], 205 [Txt|format_list_tail(T, add_indent(Ctxt, width(Txt, Ctxt)))]; 206format_1(#c_values{es=Es}, Ctxt) -> 207 format_values(Es, Ctxt); 208format_1(#c_alias{var=V,pat=P}, Ctxt) -> 209 Txt = [format(V, Ctxt)|" = "], 210 [Txt|format(P, add_indent(Ctxt, width(Txt, Ctxt)))]; 211format_1(#c_let{anno=Anno0,vars=Vs0,arg=A0,body=B}, #ctxt{clean=Clean}=Ctxt) -> 212 {Vs,A,Anno} = case Clean of 213 false -> 214 {Vs0,A0,Anno0}; 215 true -> 216 {[cerl:set_ann(V, []) || V <- Vs0], 217 cerl:set_ann(A0, []), 218 []} 219 end, 220 case is_simple_term(A) andalso Anno =:= [] of 221 false -> 222 Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent), 223 ["let ", 224 format_values(Vs, add_indent(Ctxt, 4)), 225 " =", 226 nl_indent(Ctxt1), 227 format(A, Ctxt1), 228 nl_indent(Ctxt), 229 "in " 230 | format(B, add_indent(Ctxt, 4)) 231 ]; 232 true -> 233 Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent), 234 ["let ", 235 format_values(Vs, add_indent(Ctxt, 4)), 236 " = ", 237 format(A, Ctxt1), 238 nl_indent(Ctxt), 239 "in " 240 | format(B, add_indent(Ctxt, 4)) 241 ] 242 end; 243format_1(#c_letrec{defs=Fs,body=B}, Ctxt) -> 244 Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent), 245 ["letrec", 246 nl_indent(Ctxt1), 247 format_funcs(Fs, Ctxt1), 248 nl_indent(Ctxt), 249 "in " 250 | format(B, add_indent(Ctxt, 4)) 251 ]; 252format_1(#c_seq{arg=A,body=B}, Ctxt) -> 253 Ctxt1 = add_indent(Ctxt, 4), 254 ["do ", 255 format(A, Ctxt1), 256 nl_indent(Ctxt1) 257 | format(B, Ctxt1) 258 ]; 259format_1(#c_case{arg=A,clauses=Cs}, Ctxt) -> 260 Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.item_indent), 261 ["case ", 262 format(A, add_indent(Ctxt, 5)), 263 " of", 264 nl_indent(Ctxt1), 265 format_clauses(Cs, Ctxt1), 266 nl_indent(Ctxt) 267 | "end" 268 ]; 269format_1(#c_receive{clauses=Cs,timeout=T,action=A}, Ctxt) -> 270 Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.item_indent), 271 ["receive", 272 nl_indent(Ctxt1), 273 format_clauses(Cs, Ctxt1), 274 nl_indent(Ctxt), 275 "after ", 276 format(T, add_indent(Ctxt, 6)), 277 " ->", 278 nl_indent(Ctxt1), 279 format(A, Ctxt1) 280 ]; 281format_1(#c_fun{vars=Vs,body=B}, Ctxt) -> 282 Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent), 283 ["fun (", 284 format_hseq(Vs, ",", add_indent(Ctxt, 5), fun format/2), 285 ") ->", 286 nl_indent(Ctxt1) 287 | format(B, Ctxt1) 288 ]; 289format_1(#c_apply{op=O,args=As}, Ctxt0) -> 290 Ctxt1 = add_indent(Ctxt0, 6), %"apply " 291 Op = format(O, Ctxt1), 292 Ctxt2 = add_indent(Ctxt0, 4), 293 ["apply ",Op, 294 nl_indent(Ctxt2), 295 $(,format_hseq(As, ", ", add_indent(Ctxt2, 1), fun format/2),$) 296 ]; 297format_1(#c_call{module=M,name=N,args=As}, Ctxt0) -> 298 Ctxt1 = add_indent(Ctxt0, 5), %"call " 299 Mod = format(M, Ctxt1), 300 Ctxt2 = add_indent(Ctxt1, width(Mod, Ctxt1)+1), 301 Name = format(N, Ctxt2), 302 Ctxt3 = add_indent(Ctxt0, 4), 303 ["call ",Mod,":",Name, 304 nl_indent(Ctxt3), 305 $(,format_hseq(As, ", ", add_indent(Ctxt3, 1), fun format/2),$) 306 ]; 307format_1(#c_primop{name=N,args=As}, Ctxt0) -> 308 Ctxt1 = add_indent(Ctxt0, 7), %"primop " 309 Name = format(N, Ctxt1), 310 Ctxt2 = add_indent(Ctxt0, 4), 311 ["primop ",Name, 312 nl_indent(Ctxt2), 313 $(,format_hseq(As, ", ", add_indent(Ctxt2, 1), fun format/2),$) 314 ]; 315format_1(#c_catch{body=B}, Ctxt) -> 316 Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent), 317 ["catch", 318 nl_indent(Ctxt1), 319 format(B, Ctxt1) 320 ]; 321format_1(#c_try{arg=E,vars=Vs,body=B,evars=Evs,handler=H}, Ctxt) -> 322 Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent), 323 ["try", 324 nl_indent(Ctxt1), 325 format(E, Ctxt1), 326 nl_indent(Ctxt), 327 "of ", 328 format_values(Vs, add_indent(Ctxt, 3)), 329 " ->", 330 nl_indent(Ctxt1), 331 format(B, Ctxt1), 332 nl_indent(Ctxt), 333 "catch ", 334 format_values(Evs, add_indent(Ctxt, 6)), 335 " ->", 336 nl_indent(Ctxt1) 337 | format(H, Ctxt1) 338 ]; 339format_1(#c_module{name=N,exports=Es,attrs=As,defs=Ds}, Ctxt) -> 340 Mod = ["module ", format(N, Ctxt)], 341 [Mod," [", 342 format_vseq(Es, 343 "", ",", 344 add_indent(Ctxt, width(Mod, Ctxt)+2), 345 fun format/2), 346 "]", 347 nl_indent(Ctxt), 348 " attributes [", 349 format_vseq(As, 350 "", ",", 351 add_indent(Ctxt, 16), 352 fun format_def/2), 353 "]", 354 nl_indent(Ctxt), 355 format_funcs(Ds, Ctxt), 356 nl_indent(Ctxt) 357 | "end" 358 ]. 359 360format_funcs(Fs, Ctxt) -> 361 format_vseq(Fs, 362 "", "", 363 Ctxt, 364 fun format_def/2). 365 366format_def({N,V}, Ctxt0) -> 367 Ctxt1 = add_indent(Ctxt0, Ctxt0#ctxt.body_indent), 368 [format(N, Ctxt0), 369 " =", 370 nl_indent(Ctxt1) 371 | format(V, Ctxt1) 372 ]. 373 374 375format_values(Vs, Ctxt) -> 376 [$<, 377 format_hseq(Vs, ",", add_indent(Ctxt, 1), fun format/2), 378 $>]. 379 380format_bitstr(Node, Ctxt) -> 381 maybe_anno(Node, fun do_format_bitstr/2, Ctxt). 382 383do_format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) -> 384 Vs = [S, U, T, Fs], 385 Ctxt1 = add_indent(Ctxt0, 2), 386 Val = format(V, Ctxt1), 387 Ctxt2 = add_indent(Ctxt1, width(Val, Ctxt1) + 2), 388 ["#<", Val, ">(", format_hseq(Vs,",", Ctxt2, fun format/2), $)]. 389 390format_clauses(Cs, Ctxt) -> 391 format_vseq(Cs, "", "", Ctxt, fun format_clause/2). 392 393format_clause(Node, Ctxt) -> 394 maybe_anno(Node, fun format_clause_1/2, Ctxt). 395 396format_clause_1(#c_clause{pats=Ps,guard=G,body=B}, Ctxt) -> 397 Ptxt = format_values(Ps, Ctxt), 398 Ctxt2 = add_indent(Ctxt, Ctxt#ctxt.body_indent), 399 [Ptxt, 400 case is_trivial_guard(G) of 401 true -> 402 [" when ", 403 format_guard(G, add_indent(Ctxt, width(Ptxt, Ctxt) + 6))]; 404 false -> 405 [nl_indent(Ctxt2), "when ", 406 format_guard(G, add_indent(Ctxt2, 2))] 407 end++ 408 " ->", 409 nl_indent(Ctxt2) | format(B, Ctxt2) 410 ]. 411 412is_trivial_guard(#c_literal{val=Val}) when is_atom(Val) -> true; 413is_trivial_guard(_) -> false. 414 415format_guard(Node, Ctxt) -> 416 maybe_anno(Node, fun format_guard_1/2, Ctxt). 417 418format_guard_1(#c_call{module=M,name=N,args=As}, Ctxt0) -> 419 Ctxt1 = add_indent(Ctxt0, 5), %"call " 420 Mod = format(M, Ctxt1), 421 Ctxt2 = add_indent(Ctxt1, width(Mod, Ctxt1)+1), 422 Name = format(N, Ctxt2), 423 Ctxt3 = add_indent(Ctxt0, 4), 424 ["call ",Mod,":",Name, 425 nl_indent(Ctxt3), 426 $(,format_vseq(As, "",",", add_indent(Ctxt3, 1), fun format_guard/2),$) 427 ]; 428format_guard_1(E, Ctxt) -> format_1(E, Ctxt). %Anno already done 429 430%% format_hseq([Thing], Separator, Context, Fun) -> Txt. 431%% Format a sequence horizontally on the same line with Separator between. 432 433format_hseq([H], _, Ctxt, Fun) -> 434 Fun(H, Ctxt); 435format_hseq([H|T], Sep, Ctxt, Fun) -> 436 Txt = [Fun(H, Ctxt)|Sep], 437 Ctxt1 = add_indent(Ctxt, width(Txt, Ctxt)), 438 [Txt|format_hseq(T, Sep, Ctxt1, Fun)]; 439format_hseq([], _, _, _) -> "". 440 441%% format_vseq([Thing], LinePrefix, LineSuffix, Context, Fun) -> Txt. 442%% Format a sequence vertically in indented lines adding LinePrefix 443%% to the beginning of each line and LineSuffix to the end of each 444%% line. No prefix on the first line or suffix on the last line. 445 446format_vseq([H], _Pre, _Suf, Ctxt, Fun) -> 447 Fun(H, Ctxt); 448format_vseq([H|T], Pre, Suf, Ctxt, Fun) -> 449 [Fun(H, Ctxt),Suf,nl_indent(Ctxt),Pre| 450 format_vseq(T, Pre, Suf, Ctxt, Fun)]; 451format_vseq([], _, _, _, _) -> "". 452 453format_list_tail(#c_literal{anno=[],val=[]}, _) -> "]"; 454format_list_tail(#c_cons{anno=[],hd=H,tl=T}, Ctxt) -> 455 Txt = [$,|format(H, Ctxt)], 456 Ctxt1 = add_indent(Ctxt, width(Txt, Ctxt)), 457 [Txt|format_list_tail(T, Ctxt1)]; 458format_list_tail(Tail, Ctxt) -> 459 ["|",format(Tail, add_indent(Ctxt, 1)),"]"]. 460 461format_map_pair(Op, K, V, Ctxt0) -> 462 Ctxt1 = add_indent(Ctxt0, 1), 463 Txt = format(K, Ctxt1), 464 Ctxt2 = add_indent(Ctxt0, width(Txt, Ctxt1)), 465 [Txt,Op,format(V, Ctxt2)]. 466 467indent(#ctxt{indent=N}) -> 468 if 469 N =< 0 -> 470 ""; 471 true -> 472 lists:duplicate(N div ?TAB_WIDTH, $\t) ++ spaces(N rem ?TAB_WIDTH) 473 end. 474 475nl_indent(Ctxt) -> [$\n|indent(Ctxt)]. 476 477spaces(0) -> ""; 478spaces(1) -> " "; 479spaces(2) -> " "; 480spaces(3) -> " "; 481spaces(4) -> " "; 482spaces(5) -> " "; 483spaces(6) -> " "; 484spaces(7) -> " ". 485 486%% Undo indentation done by nl_indent/1. 487unindent(T, Ctxt) -> 488 unindent(T, Ctxt#ctxt.indent, []). 489 490unindent(T, N, C) when N =< 0 -> 491 [T|C]; 492unindent([$\s|T], N, C) -> 493 unindent(T, N - 1, C); 494unindent([$\t|T], N, C) -> 495 Tab = ?TAB_WIDTH, 496 if N >= Tab -> 497 unindent(T, N - Tab, C); 498 true -> 499 unindent([spaces(Tab - N)|T], 0, C) 500 end; 501unindent([L|T], N, C) when is_list(L) -> 502 unindent(L, N, [T|C]). 503 504 505width(Txt, Ctxt) -> 506 width(Txt, 0, Ctxt, []). 507 508width([$\t|T], A, Ctxt, C) -> 509 width(T, A + ?TAB_WIDTH, Ctxt, C); 510width([$\n|T], _, Ctxt, C) -> 511 width(unindent([T|C], Ctxt), Ctxt); 512width([H|T], A, Ctxt, C) when is_list(H) -> 513 width(H, A, Ctxt, [T|C]); 514width([_|T], A, Ctxt, C) -> 515 width(T, A + 1, Ctxt, C); 516width([], A, Ctxt, [H|T]) -> 517 width(H, A, Ctxt, T); 518width([], A, _, []) -> A. 519 520add_indent(Ctxt, Dx) -> 521 Ctxt#ctxt{indent = Ctxt#ctxt.indent + Dx}. 522 523core_atom(A) -> io_lib:write_string(atom_to_list(A), $'). 524 525 526is_simple_term(#c_tuple{es=Es}) -> 527 length(Es) < 4 andalso lists:all(fun is_simple_term/1, Es); 528is_simple_term(#c_var{}) -> true; 529is_simple_term(#c_literal{val=[_|_]}) -> false; 530is_simple_term(#c_literal{val=V}) -> not is_tuple(V); 531is_simple_term(_) -> false. 532 533segs_from_bitstring(<<H,T/bitstring>>) -> 534 [#c_bitstr{val=#c_literal{val=H}, 535 size=#c_literal{val=8}, 536 unit=#c_literal{val=1}, 537 type=#c_literal{val=integer}, 538 flags=#c_literal{val=[unsigned,big]}}|segs_from_bitstring(T)]; 539segs_from_bitstring(<<>>) -> 540 []; 541segs_from_bitstring(Bitstring) -> 542 N = bit_size(Bitstring), 543 <<I:N>> = Bitstring, 544 [#c_bitstr{val=#c_literal{val=I}, 545 size=#c_literal{val=N}, 546 unit=#c_literal{val=1}, 547 type=#c_literal{val=integer}, 548 flags=#c_literal{val=[unsigned,big]}}]. 549