pretty.red 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. module pretty; % Print list structures in an indented format.
  2. % Author: A. C. Norman, July 1978.
  3. create!-package('(pretty),'(util));
  4. fluid '(bn
  5. bufferi
  6. buffero
  7. indblanks
  8. indentlevel
  9. initialblanks
  10. lmar
  11. pendingrpars
  12. rmar
  13. rparcount
  14. stack);
  15. global '(!*quotes !*pretty!-symmetric thin!*);
  16. !*pretty!-symmetric := t;
  17. !*quotes := t;
  18. thin!* := 5;
  19. % This package prints list structures in an indented format that
  20. % is intended to make them legible. There are a number of special
  21. % cases recognized, but in general the intent of the algorithm
  22. % is that given a list (R1 R2 R3 ...), SUPERPRINT checks if
  23. % the list will fit directly on the current line and if so
  24. % prints it as:
  25. % (R1 R2 R3 ...)
  26. % if not it prints it as:
  27. % (R1
  28. % R2
  29. % R3
  30. % ... )
  31. % where each sublist is similarly treated.
  32. %
  33. % Functions:
  34. % SUPERPRINTM(X,M) print expression X with left margin M
  35. % PRETTYPRINT(X) = <<superprintm(x,posn()); terpri(); terpri()>>;
  36. %
  37. % Flag:
  38. % !*SYMMETRIC If TRUE, print with escape characters,
  39. % otherwise do not (as PRIN1/PRIN2
  40. % distinction). defaults to TRUE;
  41. % !*QUOTES If TRUE, (QUOTE x) gets displayed as 'x.
  42. % default is TRUE;
  43. %
  44. % Variable:
  45. % THIN!* if THIN!* expressions can be fitted onto
  46. % a single line they will be printed that way.
  47. % this is a parameter used to control the
  48. % formatting of long thin lists. default
  49. % value is 5;
  50. symbolic procedure prettyprint x;
  51. << superprinm(x,posn()); %WHAT REDUCE DOES NOW;
  52. terpri();
  53. nil>>;
  54. symbolic procedure superprintm(x,lmar);
  55. << superprinm(x,lmar); terpri(); x >>;
  56. % From here down the functions are not intended for direct use.
  57. % The following functions are defined here in case this package
  58. % is called from LISP rather than REDUCE.
  59. symbolic procedure eqcar(a,b);
  60. pairp a and car a eq b;
  61. symbolic procedure spaces n;
  62. for i:=1:n do prin2 '! ;
  63. % End of compatibility section.
  64. symbolic procedure superprinm(x,lmar);
  65. begin
  66. scalar stack,bufferi,buffero,bn,initialblanks,rmar,
  67. pendingrpars,indentlevel,indblanks,rparcount,w;
  68. bufferi:=buffero:=list nil; %fifo buffer.
  69. initialblanks:=0;
  70. rparcount:=0;
  71. indblanks:=0;
  72. rmar:=linelength(nil) - 3; %right margin.
  73. if rmar<25 then error(0,list(rmar+3,
  74. "Linelength too short for superprinting"));
  75. bn:=0; %characters in buffer.
  76. indentlevel:=0; %no indentation needed, yet.
  77. if lmar+20>=rmar then lmar:=rmar - 21; %no room for specified margin
  78. w:=posn();
  79. if w>lmar then << terpri(); w:=0 >>;
  80. if w<lmar then initialblanks:=lmar - w;
  81. prindent(x,lmar+3); %main recursive print routine.
  82. % traverse routine finished - now tidy up buffers.
  83. overflow 'none; %flush out the buffer.
  84. return x
  85. end;
  86. % Access functions for a stack entry.
  87. smacro procedure top; car stack;
  88. smacro procedure depth frm; car frm;
  89. smacro procedure indenting frm; cadr frm;
  90. smacro procedure blankcount frm; caddr frm;
  91. smacro procedure blanklist frm; cdddr frm;
  92. smacro procedure setindenting(frm,val); rplaca(cdr frm,val);
  93. smacro procedure setblankcount(frm,val); rplaca(cddr frm,val);
  94. smacro procedure setblanklist(frm,val); rplacd(cddr frm,val);
  95. smacro procedure newframe n; list(n,nil,0);
  96. smacro procedure blankp char; numberp car char;
  97. symbolic procedure prindent(x,n);
  98. % Print list x with indentation level n.
  99. if atom x then if vectorp x then prvector(x,n)
  100. else for each c in
  101. (if !*pretty!-symmetric
  102. then if stringp x then explodes x else explode x
  103. else explode2 x) do putch c
  104. else if quotep x then <<
  105. putch '!';
  106. prindent(cadr x,n+1) >>
  107. else begin
  108. scalar cx;
  109. if 4*n>3*rmar then << %list is too deep for sanity.
  110. overflow 'all;
  111. n:=n/8;
  112. if initialblanks>n then <<
  113. lmar:=lmar - initialblanks+n;
  114. initialblanks:=n >> >>;
  115. stack := (newframe n) . stack;
  116. putch ('lpar . top());
  117. cx:=car x;
  118. prindent(cx,n+1);
  119. if idp cx and not atom cdr x then
  120. cx:=get(cx,'ppformat) else cx:=nil;
  121. if cx=2 and atom cddr x then cx:=nil;
  122. if cx='prog then <<
  123. putch '! ;
  124. prindent(car (x:=cdr x),n+3) >>;
  125. % CX now controls the formatting of what follows:
  126. % nil default action
  127. % <number> first few blanks are non-indenting
  128. % prog display atoms as labels.
  129. x:=cdr x;
  130. scan: if atom x then go to outt;
  131. finishpending(); %about to print a blank.
  132. if cx='prog then <<
  133. putblank();
  134. overflow bufferi; %force format for prog.
  135. if atom car x then << % a label.
  136. lmar:=initialblanks:=max(lmar - 6,0);
  137. prindent(car x,n - 3); % print the label.
  138. x:=cdr x;
  139. if not atom x and atom car x then go to scan;
  140. if lmar+bn>n then putblank()
  141. else for i:=lmar+bn:n - 1 do putch '! ;
  142. if atom x then go to outt>> >>
  143. else if numberp cx then <<
  144. cx:=cx - 1;
  145. if cx=0 then cx:=nil;
  146. putch '! >>
  147. else putblank();
  148. prindent(car x,n+3);
  149. x:=cdr x;
  150. go to scan;
  151. outt: if not null x then <<
  152. finishpending();
  153. putblank();
  154. putch '!.;
  155. putch '! ;
  156. prindent(x,n+5) >>;
  157. putch ('rpar . (n - 3));
  158. if indenting top()='indent and not null blanklist top() then
  159. overflow car blanklist top()
  160. else endlist top();
  161. stack:=cdr stack
  162. end;
  163. symbolic procedure explodes x;
  164. %dummy function just in case another format is needed.
  165. explode x;
  166. symbolic procedure prvector(x,n);
  167. begin
  168. scalar bound;
  169. bound:=upbv x; % length of the vector.
  170. stack:=(newframe n) . stack;
  171. putch ('lsquare . top());
  172. prindent(getv(x,0),n+3);
  173. for i:=1:bound do <<
  174. putch '!,;
  175. putblank();
  176. prindent(getv(x,i),n+3) >>;
  177. putch('rsquare . (n - 3));
  178. endlist top();
  179. stack:=cdr stack
  180. end;
  181. symbolic procedure putblank();
  182. begin
  183. putch top(); %represents a blank character.
  184. setblankcount(top(),blankcount top()+1);
  185. setblanklist(top(),bufferi . blanklist top());
  186. %remember where I was.
  187. indblanks:=indblanks+1
  188. end;
  189. symbolic procedure endlist l;
  190. %Fix up the blanks in a complete list so that they
  191. %will not be turned into indentations.
  192. pendingrpars:=l . pendingrpars;
  193. % When I have printed a ')' I want to mark all of the blanks
  194. % within the parentheses as being unindented, ordinary blank
  195. % characters. It is however possible that I may get a buffer
  196. % overflow while printing a string of )))))))))), and so this
  197. % marking should be delayed until I get round to printing
  198. % a further blank (which will be a candidate for a place to
  199. % split lines). This delay is dealt with by the list
  200. % pendingrpars which holds a list of levels that, when
  201. % convenient, can be tidied up and closed out.
  202. symbolic procedure finishpending();
  203. << for each stackframe in pendingrpars do <<
  204. if indenting stackframe neq 'indent then
  205. for each b in blanklist stackframe do
  206. << rplaca(b,'! ); indblanks:=indblanks - 1>>;
  207. % blanklist of stackframe must be non-nil so that overflow
  208. % will not treat the '(' specially.
  209. setblanklist(stackframe,t) >>;
  210. pendingrpars:=nil >>;
  211. symbolic procedure quotep x;
  212. !*quotes and
  213. not atom x and
  214. car x='quote and
  215. not atom cdr x and
  216. null cddr x;
  217. % property ppformat drives the prettyprinter -
  218. % prog : special for prog only
  219. % 1 : (fn a1
  220. % a2
  221. % ... )
  222. % 2 : (fn a1 a2
  223. % a3
  224. % ... ) ;
  225. put('prog,'ppformat,'prog);
  226. put('lambda,'ppformat,1);
  227. put('lambdaq,'ppformat,1);
  228. put('setq,'ppformat,1);
  229. put('set,'ppformat,1);
  230. put('while,'ppformat,1);
  231. put('t,'ppformat,1);
  232. put('de,'ppformat,2);
  233. put('df,'ppformat,2);
  234. put('dm,'ppformat,2);
  235. put('foreach,'ppformat,4); % (foreach x in y do ...) etc.
  236. % Now for the routines that buffer things on a character by character
  237. % basis, and deal with buffer overflow.
  238. symbolic procedure putch c;
  239. begin
  240. if atom c then rparcount:=0
  241. else if blankp c then << rparcount:=0; go to nocheck >>
  242. else if car c='rpar then <<
  243. rparcount:=rparcount+1;
  244. % format for a long string of rpars is:
  245. % )))) ))) ))) ))) ))) ;
  246. if rparcount>4 then << putch '! ; rparcount:=2 >> >>
  247. else rparcount:=0;
  248. while lmar+bn>=rmar do overflow 'more;
  249. nocheck:
  250. bufferi:=cdr rplacd(bufferi,list c);
  251. bn:=bn+1
  252. end;
  253. symbolic procedure overflow flg;
  254. begin
  255. scalar c,blankstoskip;
  256. %the current buffer holds so much information that it will
  257. %not all fit on a line. try to do something about it.
  258. % flg is one of:
  259. % 'none do not force more indentation
  260. % 'more force one level more indentation
  261. % <a pointer into the buffer>
  262. % prints up to and including that character, which
  263. % should be a blank.
  264. if indblanks=0 and initialblanks>3 and flg='more then <<
  265. initialblanks:=initialblanks - 3;
  266. lmar:=lmar - 3;
  267. return 'moved!-left >>;
  268. fblank:
  269. if bn=0 then <<
  270. % No blank found - can do no more for now.
  271. % If flg='more I am in trouble and so have to print
  272. % a continuation mark. in the other cases I can just exit.
  273. if not(flg = 'more) then return 'empty;
  274. if atom car buffero then
  275. % continuation mark not needed if last char printed was
  276. % special (e.g. lpar or rpar).
  277. prin2 "%+"; %continuation marker.
  278. terpri();
  279. lmar:=0;
  280. return 'continued >>
  281. else <<
  282. spaces initialblanks;
  283. initialblanks:=0 >>;
  284. buffero:=cdr buffero;
  285. bn:=bn - 1;
  286. lmar:=lmar+1;
  287. c:=car buffero;
  288. if atom c then << prin2 c; go to fblank >>
  289. else if blankp c then if not atom blankstoskip then <<
  290. prin2 '! ;
  291. indblanks:=indblanks - 1;
  292. % blankstoskip = (stack-frame . skip-count).
  293. if c eq car blankstoskip then <<
  294. rplacd(blankstoskip,cdr blankstoskip - 1);
  295. if cdr blankstoskip=0 then blankstoskip:=t >>;
  296. go to fblank >>
  297. else go to blankfound
  298. else if car c='lpar or car c='lsquare then <<
  299. prin2 get(car c,'ppchar);
  300. if flg='none then go to fblank;
  301. % now I want to flag this level for indentation.
  302. c:=cdr c; %the stack frame.
  303. if not null blanklist c then go to fblank;
  304. if depth c>indentlevel then << %new indentation.
  305. % this level has not emitted any blanks yet.
  306. indentlevel:=depth c;
  307. setindenting(c,'indent) >>;
  308. go to fblank >>
  309. else if car c='rpar or car c='rsquare then <<
  310. if cdr c<indentlevel then indentlevel:=cdr c;
  311. prin2 get(car c,'ppchar);
  312. go to fblank >>
  313. else error(0,list(c,"UNKNOWN TAG IN OVERFLOW"));
  314. blankfound:
  315. if eqcar(blanklist c,buffero) then
  316. setblanklist(c,nil);
  317. % at least one entry on blanklist ought to be valid, so if I
  318. % print the last blank I must kill blanklist totally.
  319. indblanks:=indblanks - 1;
  320. % check if next level represents new indentation.
  321. if depth c>indentlevel then <<
  322. if flg='none then << %just print an ordinary blank.
  323. prin2 '! ;
  324. go to fblank >>;
  325. % here I increase the indentation level by one.
  326. if blankstoskip then blankstoskip:=nil
  327. else <<
  328. indentlevel:=depth c;
  329. setindenting(c,'indent) >> >>;
  330. %otherwise I was indenting at that level anyway.
  331. if blankcount c>(thin!* - 1) then << %long thin list fix-up here.
  332. blankstoskip:=c . ((blankcount c) - 2);
  333. setindenting(c,'thin);
  334. setblankcount(c,1);
  335. indentlevel:=(depth c) - 1;
  336. prin2 '! ;
  337. go to fblank >>;
  338. setblankcount(c,(blankcount c) - 1);
  339. terpri();
  340. lmar:=initialblanks:=depth c;
  341. if buffero eq flg then return 'to!-flg;
  342. if blankstoskip or not (flg='more) then go to fblank;
  343. % keep going unless call was of type 'more'.
  344. return 'more; %try some more.
  345. end;
  346. put('lpar,'ppchar,'!();
  347. put('lsquare,'ppchar,'![);
  348. put('rpar,'ppchar,'!));
  349. put('rsquare,'ppchar,'!]);
  350. endmodule;
  351. end;