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 : Kernel Erlang (naive) prettyprinter 21 22-module(v3_kernel_pp). 23 24-export([format/1]). 25 26%%-define(INCLUDE_ANNOTATIONS, 1). 27 28-include("v3_kernel.hrl"). 29 30%% These are "internal" structures in sys_kernel which are here for 31%% debugging purposes. 32-record(iset, {anno=[],vars,arg,body}). 33-record(ifun, {anno=[],vars,body}). 34 35%% ====================================================================== %% 36%% format(Node) -> Text 37%% Node = coreErlang() 38%% Text = string() | [Text] 39%% 40%% Prettyprint-formats (naively) an abstract Core Erlang syntax 41%% tree. 42 43-record(ctxt, {indent = 0 :: non_neg_integer(), 44 item_indent = 2 :: non_neg_integer(), 45 body_indent = 2 :: non_neg_integer(), 46 tab_width = 8 :: non_neg_integer()}). 47 48canno(Cthing) -> element(2, Cthing). 49 50-spec format(#k_mdef{}) -> iolist(). 51 52format(Node) -> format(Node, #ctxt{}). 53 54format(Node, Ctxt) -> 55 case canno(Node) of 56 [] -> 57 format_1(Node, Ctxt); 58 [L,{file,_}] when is_integer(L) -> 59 format_1(Node, Ctxt); 60 [{L,C},{file,_}] when is_integer(L), is_integer(C) -> 61 format_1(Node, Ctxt); 62 List -> 63 format_anno(List, Ctxt, fun (Ctxt1) -> 64 format_1(Node, Ctxt1) 65 end) 66 end. 67 68 69-ifndef(INCLUDE_ANNOTATIONS). 70%% Don't include annotations (for readability). 71format_anno(_Anno, Ctxt, ObjFun) -> 72 ObjFun(Ctxt). 73-else. 74%% Include annotations (for debugging of annotations). 75format_anno(Anno, Ctxt0, ObjFun) -> 76 Ctxt1 = ctxt_bump_indent(Ctxt0, 1), 77 ["( ", 78 ObjFun(Ctxt0), 79 nl_indent(Ctxt1), 80 "-| ",io_lib:write(Anno), 81 " )"]. 82-endif. 83 84%% format_1(Kexpr, Context) -> string(). 85 86%%format_1(#k_char{val=C}, _Ctxt) -> io_lib:write_char(C); 87format_1(#k_var{name=V}, _Ctxt) -> 88 if is_atom(V) -> 89 case atom_to_list(V) of 90 [$_|Cs] -> "_X" ++ Cs; 91 [C|_Cs] = L when C >= $A, C =< $Z -> L; 92 Cs -> [$_|Cs] 93 end; 94 is_integer(V) -> [$_|integer_to_list(V)] 95 end; 96format_1(#k_cons{hd=H,tl=T}, Ctxt) -> 97 Txt = ["["|format(H, ctxt_bump_indent(Ctxt, 1))], 98 [Txt|format_list_tail(T, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))]; 99format_1(#k_tuple{es=Es}, Ctxt) -> 100 [${, 101 format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), 102 $} 103 ]; 104format_1(#k_map{var=#k_literal{val=M},op=assoc,es=Es}, Ctxt) when is_map(M), map_size(M) =:= 0 -> 105 ["~{", 106 format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), 107 "}~" 108 ]; 109format_1(#k_map{var=#k_literal{val=M},op=exact,es=Es}, Ctxt) when is_map(M), map_size(M) =:= 0 -> 110 ["::{", 111 format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), 112 "}::" 113 ]; 114format_1(#k_map{var=Var,op=assoc,es=Es}, Ctxt) -> 115 ["~{", 116 format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), 117 " | ",format_1(Var, Ctxt), 118 "}~" 119 ]; 120format_1(#k_map{var=Var,op=exact,es=Es}, Ctxt) -> 121 ["::{", 122 format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), 123 " | ",format_1(Var, Ctxt), 124 "}::" 125 ]; 126format_1(#k_map_pair{key=K,val=V}, Ctxt) -> 127 ["<",format(K, Ctxt),",",format(V, Ctxt),">"]; 128format_1(#k_binary{segs=S}, Ctxt) -> 129 ["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"]; 130format_1(#k_bin_seg{next=Next}=S, Ctxt) -> 131 [format_bin_seg_1(S, Ctxt), 132 format_bin_seg(Next, ctxt_bump_indent(Ctxt, 2))]; 133format_1(#k_bin_int{size=Sz,unit=U,flags=Fs,val=Val,next=Next}, Ctxt) -> 134 S = #k_bin_seg{size=Sz,unit=U,type=integer,flags=Fs, 135 seg=#k_literal{val=Val},next=Next}, 136 [format_bin_seg_1(S, Ctxt), 137 format_bin_seg(Next, ctxt_bump_indent(Ctxt, 2))]; 138format_1(#k_bin_end{}, _Ctxt) -> "#<>#"; 139format_1(#k_literal{val=A}, _Ctxt) when is_atom(A) -> 140 core_atom(A); 141format_1(#k_literal{val=Term}, _Ctxt) -> 142 io_lib:format("~p", [Term]); 143format_1(#k_local{name=N,arity=A}, Ctxt) -> 144 "local " ++ format_fa_pair({N,A}, Ctxt); 145format_1(#k_remote{mod=M,name=N,arity=A}, _Ctxt) -> 146 %% This is for our internal translator. 147 io_lib:format("remote ~ts:~ts/~w", [format(M),format(N),A]); 148format_1(#k_internal{name=N,arity=A}, Ctxt) -> 149 "internal " ++ format_fa_pair({N,A}, Ctxt); 150format_1(#k_seq{arg=A,body=B}, Ctxt) -> 151 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 152 ["do", 153 nl_indent(Ctxt1), 154 format(A, Ctxt1), 155 nl_indent(Ctxt), 156 "then", 157 nl_indent(Ctxt) 158 | format(B, Ctxt) 159 ]; 160format_1(#k_match{body=Bs,ret=Rs}, Ctxt) -> 161 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent), 162 ["match", 163 nl_indent(Ctxt1), 164 format(Bs, Ctxt1), 165 nl_indent(Ctxt), 166 "end", 167 format_ret(Rs, Ctxt1) 168 ]; 169format_1(#k_alt{first=O,then=T}, Ctxt) -> 170 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent), 171 ["alt", 172 nl_indent(Ctxt1), 173 format(O, Ctxt1), 174 nl_indent(Ctxt1), 175 format(T, Ctxt1)]; 176format_1(#k_letrec_goto{label=Label,first=First,then=Then,ret=Rs}, Ctxt) -> 177 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent), 178 ["letrec_goto ", 179 atom_to_list(Label), 180 nl_indent(Ctxt1), 181 format(Then, Ctxt1), 182 nl_indent(Ctxt1), 183 format(First, Ctxt1), 184 nl_indent(Ctxt), 185 "end", 186 format_ret(Rs, Ctxt1) 187 ]; 188format_1(#k_goto{label=Label}, _Ctxt) -> 189 ["goto ",atom_to_list(Label)]; 190format_1(#k_select{var=V,types=Cs}, Ctxt) -> 191 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 192 ["select ", 193 format(V, Ctxt), 194 nl_indent(Ctxt1), 195 format_vseq(Cs, "", "", Ctxt1, fun format/2) 196 ]; 197format_1(#k_type_clause{type=T,values=Cs}, Ctxt) -> 198 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 199 ["type ", 200 io_lib:write(T), 201 nl_indent(Ctxt1), 202 format_vseq(Cs, "", "", Ctxt1, fun format/2) 203 ]; 204format_1(#k_val_clause{val=Val,body=B}, Ctxt) -> 205 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 206 [format(Val, Ctxt), 207 " ->", 208 nl_indent(Ctxt1) 209 | format(B, Ctxt1) 210 ]; 211format_1(#k_guard{clauses=Gs}, Ctxt) -> 212 Ctxt1 = ctxt_bump_indent(Ctxt, 5), 213 ["when ", 214 nl_indent(Ctxt1), 215 format_vseq(Gs, "", "", Ctxt1, fun format/2)]; 216format_1(#k_guard_clause{guard=G,body=B}, Ctxt) -> 217 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 218 [format(G, Ctxt), 219 nl_indent(Ctxt), 220 "->", 221 nl_indent(Ctxt1) 222 | format(B, Ctxt1) 223 ]; 224format_1(#k_call{op=Op,args=As,ret=Rs}, Ctxt) -> 225 Txt = ["call (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)], 226 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 227 [Txt,format_args(As, Ctxt1), 228 format_ret(Rs, Ctxt1) 229 ]; 230format_1(#k_enter{op=Op,args=As}, Ctxt) -> 231 Txt = ["enter (",format(Op, ctxt_bump_indent(Ctxt, 7)),$)], 232 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 233 [Txt,format_args(As, Ctxt1)]; 234format_1(#k_bif{op=Op,args=As,ret=Rs}, Ctxt) -> 235 Txt = ["bif (",format(Op, ctxt_bump_indent(Ctxt, 5)),$)], 236 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 237 [Txt,format_args(As, Ctxt1), 238 format_ret(Rs, Ctxt1) 239 ]; 240format_1(#k_test{op=Op,args=As}, Ctxt) -> 241 Txt = ["test (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)], 242 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 243 [Txt,format_args(As, Ctxt1)]; 244format_1(#k_put{arg=A,ret=Rs}, Ctxt) -> 245 [format(A, Ctxt), 246 format_ret(Rs, ctxt_bump_indent(Ctxt, 1)) 247 ]; 248format_1(#k_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H,ret=Rs}, Ctxt) -> 249 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 250 ["try", 251 nl_indent(Ctxt1), 252 format(A, Ctxt1), 253 nl_indent(Ctxt), 254 "of ", 255 format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 3), fun format/2), 256 nl_indent(Ctxt1), 257 format(B, Ctxt1), 258 nl_indent(Ctxt), 259 "catch ", 260 format_hseq(Evs, ", ", ctxt_bump_indent(Ctxt, 6), fun format/2), 261 nl_indent(Ctxt1), 262 format(H, Ctxt1), 263 nl_indent(Ctxt), 264 "end", 265 format_ret(Rs, Ctxt) 266 ]; 267format_1(#k_try_enter{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Ctxt) -> 268 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 269 ["try_enter", 270 nl_indent(Ctxt1), 271 format(A, Ctxt1), 272 nl_indent(Ctxt), 273 "of ", 274 format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 3), fun format/2), 275 nl_indent(Ctxt1), 276 format(B, Ctxt1), 277 nl_indent(Ctxt), 278 "catch ", 279 format_hseq(Evs, ", ", ctxt_bump_indent(Ctxt, 6), fun format/2), 280 nl_indent(Ctxt1), 281 format(H, Ctxt1), 282 nl_indent(Ctxt), 283 "end" 284 ]; 285format_1(#k_catch{body=B,ret=Rs}, Ctxt) -> 286 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 287 ["catch", 288 nl_indent(Ctxt1), 289 format(B, Ctxt1), 290 nl_indent(Ctxt), 291 "end", 292 format_ret(Rs, Ctxt1) 293 ]; 294format_1(#k_break{args=As}, Ctxt) -> 295 ["<", 296 format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), 297 ">" 298 ]; 299format_1(#k_return{args=As}, Ctxt) -> 300 ["<<", 301 format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), 302 ">>" 303 ]; 304format_1(#k_fdef{func=F,arity=A,vars=Vs,body=B}, Ctxt) -> 305 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 306 ["fdef ", 307 format_fa_pair({F,A}, ctxt_bump_indent(Ctxt, 5)), 308 format_args(Vs, ctxt_bump_indent(Ctxt, 14)), 309 " =", 310 nl_indent(Ctxt1), 311 format(B, Ctxt1) 312 ]; 313format_1(#k_mdef{name=N,exports=Es,attributes=As,body=B}, Ctxt) -> 314 ["module ", 315 format(#k_literal{val=N}, ctxt_bump_indent(Ctxt, 7)), 316 nl_indent(Ctxt), 317 "export [", 318 format_vseq(Es, 319 "", ",", 320 ctxt_bump_indent(Ctxt, 8), 321 fun format_fa_pair/2), 322 "]", 323 nl_indent(Ctxt), 324 "attributes [", 325 format_vseq(As, 326 "", ",", 327 ctxt_bump_indent(Ctxt, 12), 328 fun format_attribute/2), 329 "]", 330 nl_indent(Ctxt), 331 format_vseq(B, 332 "", "", 333 Ctxt, 334 fun format/2), 335 nl_indent(Ctxt) 336 | "end" 337 ]; 338%% Internal sys_kernel structures. 339format_1(#iset{vars=Vs,arg=A,body=B}, Ctxt) -> 340 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 341 ["set <", 342 format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 5), fun format/2), 343 "> =", 344 nl_indent(Ctxt1), 345 format(A, Ctxt1), 346 nl_indent(Ctxt), 347 "in " 348 | format(B, ctxt_bump_indent(Ctxt, 2)) 349 ]; 350format_1(#ifun{vars=Vs,body=B}, Ctxt) -> 351 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 352 ["fun ", 353 format_args(Vs, ctxt_bump_indent(Ctxt, 4)), 354 " ->", 355 nl_indent(Ctxt1) 356 | format(B, Ctxt1) 357 ]; 358format_1(Type, _Ctxt) -> 359 ["** Unsupported type: ", 360 io_lib:write(Type) 361 | " **" 362 ]. 363 364%% format_ret([RetVar], Context) -> Txt. 365%% Format the return vars of kexpr. 366 367format_ret(Rs, Ctxt) -> 368 [" >> ", 369 "<", 370 format_hseq(Rs, ",", ctxt_bump_indent(Ctxt, 5), fun format/2), 371 ">"]. 372 373%% format_args([Arg], Context) -> Txt. 374%% Format arguments. 375 376format_args(As, Ctxt) -> 377 [$(,format_hseq(As, ", ", ctxt_bump_indent(Ctxt, 1), fun format/2),$)]. 378 379%% format_hseq([Thing], Separator, Context, Fun) -> Txt. 380%% Format a sequence horizontally. 381 382format_hseq([H], _Sep, Ctxt, Fun) -> 383 Fun(H, Ctxt); 384format_hseq([H|T], Sep, Ctxt, Fun) -> 385 Txt = [Fun(H, Ctxt)|Sep], 386 Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)), 387 [Txt|format_hseq(T, Sep, Ctxt1, Fun)]; 388format_hseq([], _, _, _) -> "". 389 390%% format_vseq([Thing], LinePrefix, LineSuffix, Context, Fun) -> Txt. 391%% Format a sequence vertically. 392 393format_vseq([H], _Pre, _Suf, Ctxt, Fun) -> 394 Fun(H, Ctxt); 395format_vseq([H|T], Pre, Suf, Ctxt, Fun) -> 396 [Fun(H, Ctxt),Suf,nl_indent(Ctxt),Pre| 397 format_vseq(T, Pre, Suf, Ctxt, Fun)]; 398format_vseq([], _, _, _, _) -> "". 399 400format_fa_pair({F,A}, _Ctxt) -> [core_atom(F),$/,integer_to_list(A)]. 401 402%% format_attribute({Name,Val}, Context) -> Txt. 403 404format_attribute({Name,Val}, Ctxt) when is_list(Val) -> 405 Txt = format(#k_literal{val=Name}, Ctxt), 406 Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt,Ctxt)+4), 407 [Txt," = ", 408 $[,format_vseq(Val, "", ",", Ctxt1, 409 fun (A, _C) -> io_lib:write(A) end),$] 410 ]; 411format_attribute({Name,Val}, Ctxt) -> 412 Txt = format(#k_literal{val=Name}, Ctxt), 413 [Txt," = ",io_lib:write(Val)]. 414 415format_list_tail(#k_literal{anno=[],val=[]}, _Ctxt) -> "]"; 416format_list_tail(#k_cons{anno=[],hd=H,tl=T}, Ctxt) -> 417 Txt = [$,|format(H, Ctxt)], 418 Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)), 419 [Txt|format_list_tail(T, Ctxt1)]; 420format_list_tail(Tail, Ctxt) -> 421 ["|",format(Tail, ctxt_bump_indent(Ctxt, 1)), "]"]. 422 423format_bin_seg([], _Ctx) -> ""; 424format_bin_seg(#k_bin_end{anno=[]}, _Ctxt) -> ""; 425format_bin_seg(#k_bin_seg{anno=[],next=N}=Seg, Ctxt) -> 426 Txt = [$,|format_bin_seg_1(Seg, Ctxt)], 427 [Txt|format_bin_seg(N, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))]; 428format_bin_seg(Seg, Ctxt) -> 429 ["|",format(Seg, ctxt_bump_indent(Ctxt, 2))]. 430 431format_bin_seg_1(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg}, Ctxt) -> 432 [format(Seg, Ctxt), 433 ":",format(S, Ctxt),"*",io_lib:write(U), 434 ":",io_lib:write(T), 435 [[$-,io_lib:write(F)] || F <- Fs] 436 ]. 437 438% format_bin_elements(#k_binary_cons{hd=H,tl=T,size=S,info=I}, Ctxt) -> 439% A = canno(T), 440% Fe = fun (Eh, Es, Ei, Ct) -> 441% [format(Eh, Ct),":",format(Es, Ct),"/",io_lib:write(Ei)] 442% end, 443% case T of 444% #k_zero_binary{} when A == [] -> 445% Fe(H, S, I, Ctxt); 446% #k_binary_cons{} when A == [] -> 447% Txt = [Fe(H, S, I, Ctxt)|","], 448% Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)), 449% [Txt|format_bin_elements(T, Ctxt1)]; 450% _ -> 451% Txt = [Fe(H, S, I, Ctxt)|"|"], 452% [Txt|format(T, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))] 453% end. 454 455indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt). 456 457indent(N, _Ctxt) when N =< 0 -> ""; 458indent(N, Ctxt) -> 459 T = Ctxt#ctxt.tab_width, 460 lists:duplicate(N div T, $\t) ++ lists:duplicate(N rem T, $\s). 461 462nl_indent(Ctxt) -> [$\n|indent(Ctxt)]. 463 464 465unindent(T, Ctxt) -> 466 unindent(T, Ctxt#ctxt.indent, Ctxt, []). 467 468unindent(T, N, _Ctxt, C) when N =< 0 -> 469 [T|C]; 470unindent([$\s|T], N, Ctxt, C) -> 471 unindent(T, N - 1, Ctxt, C); 472unindent([$\t|T], N, Ctxt, C) -> 473 Tab = Ctxt#ctxt.tab_width, 474 if N >= Tab -> 475 unindent(T, N - Tab, Ctxt, C); 476 true -> 477 unindent([lists:duplicate(Tab - N, $\s)|T], 0, Ctxt, C) 478 end; 479unindent([L|T], N, Ctxt, C) when is_list(L) -> 480 unindent(L, N, Ctxt, [T|C]); 481unindent([H|T], _N, _Ctxt, C) -> 482 [H|[T|C]]; 483unindent([], N, Ctxt, [H|T]) -> 484 unindent(H, N, Ctxt, T); 485unindent([], _, _, []) -> []. 486 487 488width(Txt, Ctxt) -> 489 width(Txt, 0, Ctxt, []). 490 491width([$\t|T], A, Ctxt, C) -> 492 width(T, A + Ctxt#ctxt.tab_width, Ctxt, C); 493width([$\n|T], _A, Ctxt, C) -> 494 width(unindent([T|C], Ctxt), Ctxt); 495width([H|T], A, Ctxt, C) when is_list(H) -> 496 width(H, A, Ctxt, [T|C]); 497width([_|T], A, Ctxt, C) -> 498 width(T, A + 1, Ctxt, C); 499width([], A, Ctxt, [H|T]) -> 500 width(H, A, Ctxt, T); 501width([], A, _, []) -> A. 502 503ctxt_bump_indent(Ctxt, Dx) -> 504 Ctxt#ctxt{indent=Ctxt#ctxt.indent + Dx}. 505 506core_atom(A) -> io_lib:write_string(atom_to_list(A), $'). 507