1%% ``Licensed under the Apache License, Version 2.0 (the "License"); 2%% you may not use this file except in compliance with the License. 3%% You may obtain a copy of the License at 4%% 5%% http://www.apache.org/licenses/LICENSE-2.0 6%% 7%% Unless required by applicable law or agreed to in writing, software 8%% distributed under the License is distributed on an "AS IS" BASIS, 9%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10%% See the License for the specific language governing permissions and 11%% limitations under the License. 12%% 13%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. 14%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings 15%% AB. All Rights Reserved.'' 16%% 17%% $Id: v3_kernel_pp.erl,v 1.1 2008/12/17 09:53:43 mikpe Exp $ 18%% 19%% Purpose : Kernel Erlang (naive) prettyprinter 20 21-module(v3_kernel_pp). 22 23-include("v3_kernel.hrl"). 24 25-export([format/1]). 26 27%% These are "internal" structures in sys_kernel which are here for 28%% debugging purposes. 29-record(iset, {anno=[],vars,arg,body}). 30-record(ifun, {anno=[],vars,body}). 31 32%% ====================================================================== %% 33%% format(Node) -> Text 34%% Node = coreErlang() 35%% Text = string() | [Text] 36%% 37%% Prettyprint-formats (naively) an abstract Core Erlang syntax 38%% tree. 39 40-record(ctxt, {indent = 0, 41 item_indent = 2, 42 body_indent = 2, 43 tab_width = 8}). 44 45canno(Cthing) -> element(2, Cthing). 46 47format(Node) -> format(Node, #ctxt{}). 48 49format(Node, Ctxt) -> 50 case canno(Node) of 51 [] -> 52 format_1(Node, Ctxt); 53 List -> 54 format_anno(List, Ctxt, fun (Ctxt1) -> format_1(Node, Ctxt1) end) 55 end. 56 57format_anno(Anno, Ctxt, ObjFun) -> 58 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 59 ["( ", 60 ObjFun(Ctxt1), 61 nl_indent(Ctxt1), 62 "-| ",io_lib:write(Anno), 63 " )"]. 64 65%% format_1(Kexpr, Context) -> string(). 66 67format_1(#k_atom{val=A}, _Ctxt) -> core_atom(A); 68%%format_1(#k_char{val=C}, _Ctxt) -> io_lib:write_char(C); 69format_1(#k_float{val=F}, _Ctxt) -> float_to_list(F); 70format_1(#k_int{val=I}, _Ctxt) -> integer_to_list(I); 71format_1(#k_nil{}, _Ctxt) -> "[]"; 72format_1(#k_string{val=S}, _Ctxt) -> io_lib:write_string(S); 73format_1(#k_var{name=V}, _Ctxt) -> 74 if atom(V) -> 75 case atom_to_list(V) of 76 [$_|Cs] -> "_X" ++ Cs; 77 [C|Cs] when C >= $A, C =< $Z -> [C|Cs]; 78 Cs -> [$_|Cs] 79 end; 80 integer(V) -> [$_|integer_to_list(V)] 81 end; 82format_1(#k_cons{hd=H,tl=T}, Ctxt) -> 83 Txt = ["["|format(H, ctxt_bump_indent(Ctxt, 1))], 84 [Txt|format_list_tail(T, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))]; 85format_1(#k_tuple{es=Es}, Ctxt) -> 86 [${, 87 format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), 88 $} 89 ]; 90format_1(#k_binary{segs=S}, Ctxt) -> 91 ["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"]; 92format_1(#k_bin_seg{}=S, Ctxt) -> 93 [format_bin_seg_1(S, Ctxt), 94 format_bin_seg(S#k_bin_seg.next, ctxt_bump_indent(Ctxt, 2))]; 95format_1(#k_bin_end{}, _Ctxt) -> "#<>#"; 96format_1(#k_local{name=N,arity=A}, Ctxt) -> 97 "local " ++ format_fa_pair({N,A}, Ctxt); 98format_1(#k_remote{mod=M,name=N,arity=A}, _Ctxt) -> 99 %% This is for our internal translator. 100 io_lib:format("remote ~s:~s/~w", [format(M),format(N),A]); 101format_1(#k_internal{name=N,arity=A}, Ctxt) -> 102 "internal " ++ format_fa_pair({N,A}, Ctxt); 103format_1(#k_seq{arg=A,body=B}, Ctxt) -> 104 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 105 ["do", 106 nl_indent(Ctxt1), 107 format(A, Ctxt1), 108 nl_indent(Ctxt), 109 "then", 110 nl_indent(Ctxt) 111 | format(B, Ctxt) 112 ]; 113format_1(#k_match{vars=Vs,body=Bs,ret=Rs}, Ctxt) -> 114 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent), 115 ["match ", 116 format_hseq(Vs, ",", ctxt_bump_indent(Ctxt, 6), fun format/2), 117 nl_indent(Ctxt1), 118 format(Bs, Ctxt1), 119 nl_indent(Ctxt), 120 "end", 121 format_ret(Rs, Ctxt1) 122 ]; 123format_1(#k_alt{first=O,then=T}, Ctxt) -> 124 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent), 125 ["alt", 126 nl_indent(Ctxt1), 127 format(O, Ctxt1), 128 nl_indent(Ctxt1), 129 format(T, Ctxt1)]; 130format_1(#k_select{var=V,types=Cs}, Ctxt) -> 131 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 132 ["select ", 133 format(V, Ctxt), 134 nl_indent(Ctxt1), 135 format_vseq(Cs, "", "", Ctxt1, fun format/2) 136 ]; 137format_1(#k_type_clause{type=T,values=Cs}, Ctxt) -> 138 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 139 ["type ", 140 io_lib:write(T), 141 nl_indent(Ctxt1), 142 format_vseq(Cs, "", "", Ctxt1, fun format/2) 143 ]; 144format_1(#k_val_clause{val=Val,body=B}, Ctxt) -> 145 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 146 [format(Val, Ctxt), 147 " ->", 148 nl_indent(Ctxt1) 149 | format(B, Ctxt1) 150 ]; 151format_1(#k_guard{clauses=Gs}, Ctxt) -> 152 Ctxt1 = ctxt_bump_indent(Ctxt, 5), 153 ["when ", 154 nl_indent(Ctxt1), 155 format_vseq(Gs, "", "", Ctxt1, fun format/2)]; 156format_1(#k_guard_clause{guard=G,body=B}, Ctxt) -> 157 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 158 [format(G, Ctxt), 159 nl_indent(Ctxt), 160 "->", 161 nl_indent(Ctxt1) 162 | format(B, Ctxt1) 163 ]; 164format_1(#k_call{op=Op,args=As,ret=Rs}, Ctxt) -> 165 Txt = ["call (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)], 166 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 167 [Txt,format_args(As, Ctxt1), 168 format_ret(Rs, Ctxt1) 169 ]; 170format_1(#k_enter{op=Op,args=As}, Ctxt) -> 171 Txt = ["enter (",format(Op, ctxt_bump_indent(Ctxt, 7)),$)], 172 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 173 [Txt,format_args(As, Ctxt1)]; 174format_1(#k_bif{op=Op,args=As,ret=Rs}, Ctxt) -> 175 Txt = ["bif (",format(Op, ctxt_bump_indent(Ctxt, 5)),$)], 176 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 177 [Txt,format_args(As, Ctxt1), 178 format_ret(Rs, Ctxt1) 179 ]; 180format_1(#k_test{op=Op,args=As}, Ctxt) -> 181 Txt = ["test (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)], 182 Ctxt1 = ctxt_bump_indent(Ctxt, 2), 183 [Txt,format_args(As, Ctxt1)]; 184format_1(#k_put{arg=A,ret=Rs}, Ctxt) -> 185 [format(A, Ctxt), 186 format_ret(Rs, ctxt_bump_indent(Ctxt, 1)) 187 ]; 188format_1(#k_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H,ret=Rs}, Ctxt) -> 189 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 190 ["try", 191 nl_indent(Ctxt1), 192 format(A, Ctxt1), 193 nl_indent(Ctxt), 194 "of ", 195 format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 3), fun format/2), 196 nl_indent(Ctxt1), 197 format(B, Ctxt1), 198 nl_indent(Ctxt), 199 "catch ", 200 format_hseq(Evs, ", ", ctxt_bump_indent(Ctxt, 6), fun format/2), 201 nl_indent(Ctxt1), 202 format(H, Ctxt1), 203 nl_indent(Ctxt), 204 "end", 205 format_ret(Rs, Ctxt1) 206 ]; 207format_1(#k_catch{body=B,ret=Rs}, Ctxt) -> 208 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 209 ["catch", 210 nl_indent(Ctxt1), 211 format(B, Ctxt1), 212 nl_indent(Ctxt), 213 "end", 214 format_ret(Rs, Ctxt1) 215 ]; 216format_1(#k_receive{var=V,body=B,timeout=T,action=A,ret=Rs}, Ctxt) -> 217 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent), 218 ["receive ", 219 format(V, Ctxt), 220 nl_indent(Ctxt1), 221 format(B, Ctxt1), 222 nl_indent(Ctxt), 223 "after ", 224 format(T, ctxt_bump_indent(Ctxt, 6)), 225 " ->", 226 nl_indent(Ctxt1), 227 format(A, Ctxt1), 228 nl_indent(Ctxt), 229 "end", 230 format_ret(Rs, Ctxt1) 231 ]; 232format_1(#k_receive_accept{}, _Ctxt) -> "receive_accept"; 233format_1(#k_receive_next{}, _Ctxt) -> "receive_next"; 234format_1(#k_break{args=As}, Ctxt) -> 235 ["<", 236 format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), 237 ">" 238 ]; 239format_1(#k_return{args=As}, Ctxt) -> 240 ["<<", 241 format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2), 242 ">>" 243 ]; 244format_1(#k_fdef{func=F,arity=A,vars=Vs,body=B}, Ctxt) -> 245 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 246 ["fdef ", 247 format_fa_pair({F,A}, ctxt_bump_indent(Ctxt, 5)), 248 format_args(Vs, ctxt_bump_indent(Ctxt, 14)), 249 " =", 250 nl_indent(Ctxt1), 251 format(B, Ctxt1) 252 ]; 253format_1(#k_mdef{name=N,exports=Es,attributes=As,body=B}, Ctxt) -> 254 ["module ", 255 format(#k_atom{val=N}, ctxt_bump_indent(Ctxt, 7)), 256 nl_indent(Ctxt), 257 "export [", 258 format_vseq(Es, 259 "", ",", 260 ctxt_bump_indent(Ctxt, 8), 261 fun format_fa_pair/2), 262 "]", 263 nl_indent(Ctxt), 264 "attributes [", 265 format_vseq(As, 266 "", ",", 267 ctxt_bump_indent(Ctxt, 12), 268 fun format_attribute/2), 269 "]", 270 nl_indent(Ctxt), 271 format_vseq(B, 272 "", "", 273 Ctxt, 274 fun format/2), 275 nl_indent(Ctxt) 276 | "end" 277 ]; 278%% Internal sys_kernel structures. 279format_1(#iset{vars=Vs,arg=A,body=B}, Ctxt) -> 280 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 281 ["set <", 282 format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 5), fun format/2), 283 "> =", 284 nl_indent(Ctxt1), 285 format(A, Ctxt1), 286 nl_indent(Ctxt), 287 "in " 288 | format(B, ctxt_bump_indent(Ctxt, 2)) 289 ]; 290format_1(#ifun{vars=Vs,body=B}, Ctxt) -> 291 Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent), 292 ["fun ", 293 format_args(Vs, ctxt_bump_indent(Ctxt, 4)), 294 " ->", 295 nl_indent(Ctxt1) 296 | format(B, Ctxt1) 297 ]; 298format_1(Type, _Ctxt) -> 299 ["** Unsupported type: ", 300 io_lib:write(Type) 301 | " **" 302 ]. 303 304%% format_ret([RetVar], Context) -> Txt. 305%% Format the return vars of kexpr. 306 307format_ret(Rs, Ctxt) -> 308 [" >> ", 309 "<", 310 format_hseq(Rs, ",", ctxt_bump_indent(Ctxt, 5), fun format/2), 311 ">"]. 312 313%% format_args([Arg], Context) -> Txt. 314%% Format arguments. 315 316format_args(As, Ctxt) -> 317 [$(,format_hseq(As, ", ", ctxt_bump_indent(Ctxt, 1), fun format/2),$)]. 318 319%% format_hseq([Thing], Separator, Context, Fun) -> Txt. 320%% Format a sequence horizontally. 321 322format_hseq([H], _Sep, Ctxt, Fun) -> 323 Fun(H, Ctxt); 324format_hseq([H|T], Sep, Ctxt, Fun) -> 325 Txt = [Fun(H, Ctxt)|Sep], 326 Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)), 327 [Txt|format_hseq(T, Sep, Ctxt1, Fun)]; 328format_hseq([], _, _, _) -> "". 329 330%% format_vseq([Thing], LinePrefix, LineSuffix, Context, Fun) -> Txt. 331%% Format a sequence vertically. 332 333format_vseq([H], _Pre, _Suf, Ctxt, Fun) -> 334 Fun(H, Ctxt); 335format_vseq([H|T], Pre, Suf, Ctxt, Fun) -> 336 [Fun(H, Ctxt),Suf,nl_indent(Ctxt),Pre| 337 format_vseq(T, Pre, Suf, Ctxt, Fun)]; 338format_vseq([], _, _, _, _) -> "". 339 340format_fa_pair({F,A}, _Ctxt) -> [core_atom(F),$/,integer_to_list(A)]. 341 342%% format_attribute({Name,Val}, Context) -> Txt. 343 344format_attribute({Name,Val}, Ctxt) when list(Val) -> 345 Txt = format(#k_atom{val=Name}, Ctxt), 346 Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt,Ctxt)+4), 347 [Txt," = ", 348 $[,format_vseq(Val, "", ",", Ctxt1, 349 fun (A, _C) -> io_lib:write(A) end),$] 350 ]; 351format_attribute({Name,Val}, Ctxt) -> 352 Txt = format(#k_atom{val=Name}, Ctxt), 353 [Txt," = ",io_lib:write(Val)]. 354 355format_list_tail(#k_nil{anno=[]}, _Ctxt) -> "]"; 356format_list_tail(#k_cons{anno=[],hd=H,tl=T}, Ctxt) -> 357 Txt = [$,|format(H, Ctxt)], 358 Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)), 359 [Txt|format_list_tail(T, Ctxt1)]; 360format_list_tail(Tail, Ctxt) -> 361 ["|",format(Tail, ctxt_bump_indent(Ctxt, 1)), "]"]. 362 363format_bin_seg(#k_bin_end{anno=[]}, _Ctxt) -> ""; 364format_bin_seg(#k_bin_seg{anno=[],next=N}=Seg, Ctxt) -> 365 Txt = [$,|format_bin_seg_1(Seg, Ctxt)], 366 [Txt|format_bin_seg(N, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))]; 367format_bin_seg(Seg, Ctxt) -> 368 ["|",format(Seg, ctxt_bump_indent(Ctxt, 2))]. 369 370format_bin_seg_1(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg}, Ctxt) -> 371 [format(Seg, Ctxt), 372 ":",format(S, Ctxt),"*",io_lib:write(U), 373 ":",io_lib:write(T), 374 lists:map(fun (F) -> [$-,io_lib:write(F)] end, Fs) 375 ]. 376 377% format_bin_elements(#k_binary_cons{hd=H,tl=T,size=S,info=I}, Ctxt) -> 378% A = canno(T), 379% Fe = fun (Eh, Es, Ei, Ct) -> 380% [format(Eh, Ct),":",format(Es, Ct),"/",io_lib:write(Ei)] 381% end, 382% case T of 383% #k_zero_binary{} when A == [] -> 384% Fe(H, S, I, Ctxt); 385% #k_binary_cons{} when A == [] -> 386% Txt = [Fe(H, S, I, Ctxt)|","], 387% Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)), 388% [Txt|format_bin_elements(T, Ctxt1)]; 389% _ -> 390% Txt = [Fe(H, S, I, Ctxt)|"|"], 391% [Txt|format(T, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))] 392% end. 393 394indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt). 395 396indent(N, _Ctxt) when N =< 0 -> ""; 397indent(N, Ctxt) -> 398 T = Ctxt#ctxt.tab_width, 399 string:chars($\t, N div T, string:chars($\s, N rem T)). 400 401nl_indent(Ctxt) -> [$\n|indent(Ctxt)]. 402 403 404unindent(T, Ctxt) -> 405 unindent(T, Ctxt#ctxt.indent, Ctxt, []). 406 407unindent(T, N, _Ctxt, C) when N =< 0 -> 408 [T|C]; 409unindent([$\s|T], N, Ctxt, C) -> 410 unindent(T, N - 1, Ctxt, C); 411unindent([$\t|T], N, Ctxt, C) -> 412 Tab = Ctxt#ctxt.tab_width, 413 if N >= Tab -> 414 unindent(T, N - Tab, Ctxt, C); 415 true -> 416 unindent([string:chars($\s, Tab - N)|T], 0, Ctxt, C) 417 end; 418unindent([L|T], N, Ctxt, C) when list(L) -> 419 unindent(L, N, Ctxt, [T|C]); 420unindent([H|T], _N, _Ctxt, C) -> 421 [H|[T|C]]; 422unindent([], N, Ctxt, [H|T]) -> 423 unindent(H, N, Ctxt, T); 424unindent([], _, _, []) -> []. 425 426 427width(Txt, Ctxt) -> 428 width(Txt, 0, Ctxt, []). 429 430width([$\t|T], A, Ctxt, C) -> 431 width(T, A + Ctxt#ctxt.tab_width, Ctxt, C); 432width([$\n|T], _A, Ctxt, C) -> 433 width(unindent([T|C], Ctxt), Ctxt); 434width([H|T], A, Ctxt, C) when list(H) -> 435 width(H, A, Ctxt, [T|C]); 436width([_|T], A, Ctxt, C) -> 437 width(T, A + 1, Ctxt, C); 438width([], A, Ctxt, [H|T]) -> 439 width(H, A, Ctxt, T); 440width([], A, _, []) -> A. 441 442ctxt_bump_indent(Ctxt, Dx) -> 443 Ctxt#ctxt{indent=Ctxt#ctxt.indent + Dx}. 444 445core_atom(A) -> io_lib:write_string(atom_to_list(A), $'). 446