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