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