extras.red 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. %
  2. % Additional useful functions to have in a Lisp environment.
  3. %
  4. % The following small function is just used for testing the CSL OEM
  5. % interface code...
  6. symbolic procedure oem!-supervisor();
  7. print eval read();
  8. %
  9. % If you go (setq !*break!-loop!* 'break!-loop) then errors will get this
  10. % function called - and it is rather desirable that it does not itself fail.
  11. % The argument is what was passed to (ERROR ...) if the Lisp-level error
  12. % function was called. When this function exits the system will unwind back
  13. % to the next enclosing ERRORSET. (enable!-backtrace <fg>) can be used to
  14. % switch backtrace display on or off.
  15. %
  16. symbolic procedure break!-loop a;
  17. begin
  18. scalar prompt, ifile, ofile, u, v;
  19. % I use wrs/rds so I am compatible between Standard and Common Lisp here
  20. ifile := rds !*debug!-io!*;
  21. ofile := wrs !*debug!-io!*;
  22. prompt := setpchar "Break loop (:X exits)> ";
  23. top:u := read();
  24. if u = '!:x then go to exit
  25. else if u = '!:q then <<
  26. enable!-backtrace nil;
  27. princ "Backtrace now disabled";
  28. terpri() >>
  29. else if u = '!:v then <<
  30. enable!-backtrace t;
  31. princ "Backtrace now enabled";
  32. terpri() >>
  33. else <<
  34. if null u then v := nil
  35. else v := errorset(u, nil, nil);
  36. if atom v then <<
  37. princ ":Q quietens backtrace"; terpri();
  38. princ ":V enables backtrace"; terpri();
  39. princ ":X exits from break loop"; terpri();
  40. princ "else form for evaluation"; terpri();
  41. >>
  42. else <<
  43. prin "=> ";
  44. prinl car v;
  45. terpri() >> >>;
  46. go to top;
  47. exit:
  48. rds ifile;
  49. wrs ofile;
  50. setpchar prompt;
  51. return nil
  52. end;
  53. % dated!-name manufactures a symbol that is expected to be unique - but
  54. % there will in fact be no strict guarantee of that. The name is made up out
  55. % of a base part provided by the caller, then a chunk that encodes the
  56. % date and time of day that the function was called (accurate to around
  57. % a second, typically). Finally a serial number that starts off as 1 when
  58. % the "extras" module is loaded into a copy of Lisp. Two copies of Lisp
  59. % running at the same time could lead to clashes here. But names of this
  60. % sort seem to be needed for inclusion in files and other places where
  61. % re-readability is vital.
  62. global '(s!:gensym!-serial);
  63. s!:gensym!-serial := 0;
  64. symbolic procedure s!:stamp n;
  65. % Converts an integer into a sequence of letters and digits by
  66. % converting to base 36 (with the digits ending up in the "wrong"
  67. % order). Used only when generating probably-unique-identifiers to
  68. % use as names for internally generated functions.
  69. if n < 0 then append(s!:stamp(-n), '(!-))
  70. else if n = 0 then nil
  71. else schar("0123456789abcdefghijklmnopqrstuvwxyz", remainder(n, 36)) .
  72. s!:stamp truncate(n ,36);
  73. symbolic procedure dated!-name base;
  74. intern list!-to!-string
  75. append(explodec base,
  76. '!_ . append(reverse s!:stamp datestamp(),
  77. '!_ . explodec(s!:gensym!-serial := s!:gensym!-serial + 1)));
  78. % hashtagged!-name(base, value) manufactures a name based on the
  79. % base together with a hash-value computed from the "value". This
  80. % is expected to be a reliable signature (but clashes are of course
  81. % possible). Eg base may be the name of a function and value its
  82. % definition, then this will invent a name suitable for a parallel
  83. % version of the function where the new name ought not to conflict with
  84. % ones used later if this function gets defined with a different
  85. % definition.
  86. symbolic procedure hashtagged!-name(base, value);
  87. intern list!-to!-string
  88. append(explodec base, '!_ . s!:stamp md60 value);
  89. %
  90. % Sorting
  91. %
  92. remflag('(sort sortip), 'lose);
  93. symbolic procedure sort(l, pred);
  94. % Sort the list l according to the given predicate. If l is a list
  95. % of numbers then the predicate "lessp" will sort the list into
  96. % ascending order. The predicate should be a strict inequality, i.e.
  97. % it should return NIL if the two items compared are equal.
  98. % As implemented here SORT just calls STABLE-SORT, but as a matter of
  99. % style any use where the ordering of incomparable items in the output
  100. % matters ought to use STABLE!-SORT directly, thereby allowing the
  101. % replacement of this code with a faster non-stable method.
  102. % (Note: the previous REDUCE sort function also happened to be stable, so
  103. % this code should give exactly the same results for all calls where
  104. % the predicate is self-consistent and never has both pred(a,b) and
  105. % pred(b,a) true. A previous CSL sort was not stable, but was perhaps
  106. % very slightly faster than this)
  107. stable!-sortip(append(l, nil), pred);
  108. symbolic procedure stable!-sort(l, pred);
  109. % Sorts a list, as SORT, but if two items x and y in the input list
  110. % satisfy neither pred(x,y) nor pred(y,x) [i.e. they are equal so far
  111. % as the given ordering predicate is concerned] this function guarantees
  112. % that they will appear in the output list in the same order that they
  113. % were in the input.
  114. stable!-sortip(append(l, nil), pred);
  115. symbolic procedure sortip(l, pred);
  116. stable!-sortip(l, pred);
  117. symbolic procedure stable!-sortip(l, pred);
  118. % As stable!-sort, but over-writes the input list to make the output.
  119. % It is not intended that people should call this function directly: it
  120. % is present just as the implementation of the main sort procedures defined
  121. % above.
  122. begin
  123. scalar l1, l2, w;
  124. if null l then return l; % Input list of length 0
  125. l1 := l;
  126. l2 := cdr l;
  127. if null l2 then return l; % Input list of length 1
  128. % Now I have dealt with the essential special cases of lists of length 0
  129. % and 1 (which do not need sorting at all). Since it possibly speeds things
  130. % up just a little I will now have some fairly ugly code that makes special
  131. % cases of lists of length 2. I could easily have special code for length
  132. % 3 lists here (and include it, but commented out), but at present my
  133. % measurements suggest that the speed improvement that it gives is minimal
  134. % and the increase in code bulk is large enough to give some pain.
  135. l := cdr l2;
  136. if null l then << % Input list of length 2
  137. if apply2(pred, car l2, car l1) then <<
  138. l := car l1;
  139. rplaca(l1, car l2);
  140. rplaca(l2, l) >>;
  141. return l1 >>;
  142. % Now I will check to see if the list is in fact in order already
  143. % Doing so will have a cost - but sometimes that cost will be repaid
  144. % when I am able to exit especially early. The result of all this
  145. % is that I will have a best case behaviour with linear cost growth for
  146. % inputs that are initially in the correct order, while my average and
  147. % worst-case costs will increase by a constant factor.
  148. l := l1;
  149. while l2 and not apply2(pred, car l2, car l) do <<
  150. % In the input list is NOT already in order then I expect that this
  151. % loop will exit fairly early, and so will not contribute much to the
  152. % total cost. If it exits very late then probably in the next recursion
  153. % down the first half of the list will be found to be already sorted, and
  154. % again I have a chance to win.
  155. l := l2; l2 := cdr l2 >>;
  156. if null l2 then return l1;
  157. l2 := l1;
  158. l := cddr l2;
  159. while l and cdr l do << l2 := cdr l2; l := cddr l >>;
  160. l := l2;
  161. l2 := cdr l2;
  162. rplacd(l, nil);
  163. % The two sub-lists are then sorted.
  164. l1 := stable!-sortip(l1, pred);
  165. l2 := stable!-sortip(l2, pred);
  166. % Now I merge the sorted fragments, giving priority to item from the
  167. % earlier part of the original list.
  168. l := w := list nil;
  169. while l1 and l2 do <<
  170. if apply2(pred, car l2, car l1) then <<
  171. rplacd(w, l2); w := l2; l2 := cdr l2 >>
  172. else << rplacd(w, l1); w := l1; l1 := cdr l1 >> >>;
  173. if l1 then l2 := l1;
  174. rplacd(w, l2);
  175. return cdr l
  176. end;
  177. %
  178. % Code to print potentially re-entrant lists
  179. %
  180. fluid '(!*prinl!-visited!-nodes!* !*prinl!-index!*
  181. !*prinl!-fn!* !*loop!-print!* !*print!-array!*
  182. !*print!-length!* !*print!-level!*);
  183. !*print!-length!* := !*print!-level!* := nil;
  184. !*prinl!-visited!-nodes!* := mkhash(10, 0, 1.5)$
  185. symbolic procedure s!:prinl0(x,!*prinl!-fn!*);
  186. % print x even if it has loops in it
  187. begin
  188. scalar !*prinl!-index!*;
  189. !*prinl!-index!*:=0;
  190. % Clear the hash table AFTER use, so that the junk that goes into it does
  191. % not gobble memory between calls to prinl. This relies on unwind!-protect
  192. % to make sure that it is indeed always cleared. Errors (eg ^C) during the
  193. % clean-up operation could lead to curious displays in the next use of
  194. % prinl. Also of course bugs in the implementation of unwind!-protect...
  195. % clrhash !*prinl!-visited!-nodes!*;
  196. unwind!-protect(<< s!:prinl1(x, 0); s!:prinl2(x, 0) >>,
  197. clrhash !*prinl!-visited!-nodes!*);
  198. return x
  199. end;
  200. symbolic procedure s!:prinl1(x, depth);
  201. % Find all the nodes in x and record them in the hash table.
  202. % The first time a node is met it is inserted with associated value 0.
  203. % If a node is met a second time then it is assigned an unique positive
  204. % integer code that will later be used in its label.
  205. begin
  206. scalar w, length;
  207. if fixp !*print!-level!* and depth > !*print!-level!* then return nil;
  208. length := 0;
  209. top:
  210. if atom x and not simple!-vector!-p x and not gensymp x then return nil
  211. else if w := gethash(x,!*prinl!-visited!-nodes!*) then <<
  212. if w = 0 then <<
  213. !*prinl!-index!* := !*prinl!-index!* + 1;
  214. puthash(x,!*prinl!-visited!-nodes!*, !*prinl!-index!*) >>;
  215. return nil >>
  216. else <<
  217. puthash(x, !*prinl!-visited!-nodes!*, 0);
  218. if simple!-vector!-p x then <<
  219. if !*print!-array!* then <<
  220. length := upbv x;
  221. if fixp !*print!-length!* and !*print!-length!* < length then
  222. length := !*print!-length!*;
  223. for i:=0:length do s!:prinl1(getv(x,i), depth+1) >> >>
  224. else if not atom x then <<
  225. s!:prinl1(car x, depth+1);
  226. if fixp !*print!-length!* and
  227. (length := length+1) > !*print!-length!* then return nil;
  228. x := cdr x;
  229. go to top >> >>
  230. end;
  231. symbolic procedure s!:prinl2(x, depth);
  232. % Scan a structure that was previously processed by s!:prinl1. Thus all
  233. % nodes in x are already in the hash table. Those with value zero
  234. % are only present once in x, while those with strictly positive values
  235. % occur at least twice. After printing a label for such value this resets the
  236. % value negative so that the printing can tell when the visit is for
  237. % a second rather than first time. The output format is intended to
  238. % bear some resemblance to the expectations of Common Lisp.
  239. if fixp !*print!-level!* and depth > !*print!-level!* then
  240. princ "#"
  241. else if atom x and not simple!-vector!-p x and not gensymp x then <<
  242. !#if common!-lisp!-mode
  243. if complex!-arrayp x and not !*print!-array!* then princ "[Array]"
  244. else if structp x and not !*print!-array!* then princ "[Struct]"
  245. else
  246. !#endif
  247. funcall(!*prinl!-fn!*, x) >>
  248. else begin scalar w, length;
  249. w := gethash(x,!*prinl!-visited!-nodes!*);
  250. % w has better be a number here, following s!:prinl1
  251. if not zerop w then <<
  252. if w < 0 then <<
  253. princ "#";
  254. princ (-w);
  255. princ "#";
  256. return nil >>
  257. else <<
  258. puthash(x,!*prinl!-visited!-nodes!*, -w);
  259. princ "#";
  260. princ w;
  261. princ "=" >> >>;
  262. if simple!-vector!-p x then <<
  263. princ "%(";
  264. if !*print!-array!* then <<
  265. length := upbv x;
  266. if fixp !*print!-length!* and !*print!-length!* < length then
  267. length := !*print!-length!*;
  268. for i:=0:length do << s!:prinl2(getv(x,i), depth+1);
  269. if not i=upbv x then princ " " >> >>
  270. else princ "...";
  271. princ ")";
  272. return nil >>
  273. else if atom x then return funcall(!*prinl!-fn!*, x);
  274. princ "(";
  275. length := 0;
  276. loop:
  277. s!:prinl2(car x, depth+1);
  278. x:=cdr x;
  279. if atom x then <<
  280. if simple!-vector!-p x then <<
  281. princ " . %(";
  282. if !*print!-array!* then <<
  283. length := upbv x;
  284. if fixp !*print!-length!* and !*print!-length!* < length then
  285. length := !*print!-length!*;
  286. for i:=0:length do <<s!:prinl2(getv(x,i), depth+1);
  287. if not i=upbv x then princ " ">> >>
  288. else princ "...";
  289. princ ")" >>
  290. else if x then <<
  291. princ " . ";
  292. funcall(!*prinl!-fn!*, x) >>;
  293. return princ ")" >>;
  294. if fixp !*print!-length!* and
  295. (length := length + 1) > !*print!-length!* then
  296. return princ " ...)";
  297. w := gethash(x, !*prinl!-visited!-nodes!*);
  298. if not (w = 0) then if w < 0 then <<
  299. princ " . #";
  300. princ (-w);
  301. return princ "#)" >>
  302. else <<
  303. princ " . ";
  304. s!:prinl2(x, depth+1); % This will set the label
  305. return princ ")" >>
  306. else princ " ";
  307. go to loop
  308. end;
  309. symbolic procedure printl x;
  310. << prinl x;
  311. terpri();
  312. x >>;
  313. symbolic procedure printcl x;
  314. << princl x;
  315. terpri();
  316. x >>;
  317. symbolic procedure princl x;
  318. s!:prinl0(x,function princ);
  319. symbolic procedure prinl x;
  320. s!:prinl0(x,function prin);
  321. %
  322. % A small subset of the facilities of the unreasonably baroque Common
  323. % Lisp FORMAT function may be useful.
  324. %
  325. !#if (not common!-lisp!-mode)
  326. % If I am in COMMON Lisp mode then a more complete version of this
  327. % will be installed from elsewhere.
  328. symbolic procedure s!:format(dest, fmt, args);
  329. begin
  330. scalar len, c, a, res, o;
  331. if not null dest then <<
  332. if dest = 't then o := wrs nil
  333. else o := wrs dest >>;
  334. len := upbv fmt;
  335. for i := 0:len do <<
  336. c := schar(fmt, i);
  337. if c = '!~ then <<
  338. i := i + 1;
  339. c := char!-downcase schar(fmt, i);
  340. if c = '!% then
  341. if null dest then res := !$eol!$ . res
  342. else terpri()
  343. else if c = '!~ then
  344. if null dest then res := '!~ . res
  345. else princ '!~
  346. else <<
  347. if null args then a := nil
  348. else <<
  349. a := car args;
  350. args := cdr args >>;
  351. if c = '!a then
  352. if null dest then for each k in explode2 a do res := k . res
  353. else princ a
  354. else if c = '!s then
  355. if null dest then for each k in explode a do res := k . res
  356. else prin a
  357. else
  358. if null dest then for each k in explode a do res := k . res
  359. else prin list('!?!?!?, c, a) >> >>
  360. else <<
  361. if null dest then res := c . res
  362. else princ c >> >>;
  363. if null dest then return list!-to!-string reversip res
  364. else << wrs o; return nil >>
  365. end;
  366. symbolic macro procedure format u;
  367. list('s!:format, cadr u, caddr u, 'list . cdddr u);
  368. !#endif
  369. fluid '(bn
  370. bufferi
  371. buffero
  372. indblanks
  373. indentlevel
  374. initialblanks
  375. lmar
  376. pendingrpars
  377. rmar
  378. rparcount
  379. stack);
  380. global '(!*quotes !*pretty!-symmetric thin!*);
  381. !*pretty!-symmetric := t;
  382. !*quotes := t;
  383. thin!* := 5;
  384. % This package prints list structures in an indented format that
  385. % is intended to make them legible. There are a number of special
  386. % cases recognized, but in general the intent of the algorithm
  387. % is that given a list (R1 R2 R3 ...), SUPERPRINT checks if
  388. % the list will fit directly on the current line and if so
  389. % prints it as:
  390. % (R1 R2 R3 ...)
  391. % if not it prints it as:
  392. % (R1
  393. % R2
  394. % R3
  395. % ... )
  396. % where each sublist is similarly treated.
  397. %
  398. % Functions:
  399. % SUPERPRINTM(X,M) print expression X with left margin M
  400. % PRETTYPRINT(X) = <<superprintm(x,posn()); terpri(); terpri()>>;
  401. %
  402. % Flag:
  403. % !*SYMMETRIC If TRUE, print with escape characters,
  404. % otherwise do not (as PRIN1/PRIN2
  405. % distinction). defaults to TRUE;
  406. % !*QUOTES If TRUE, (QUOTE x) gets displayed as 'x.
  407. % default is TRUE;
  408. %
  409. % Variable:
  410. % THIN!* if THIN!* expressions can be fitted onto
  411. % a single line they will be printed that way.
  412. % this is a parameter used to control the
  413. % formatting of long thin lists. default
  414. % value is 5;
  415. symbolic procedure prettyprint x;
  416. << superprinm(x,posn()); % What REDUCE seems to want. Looks a bit odd to me!
  417. terpri();
  418. nil>>;
  419. symbolic procedure superprintm(x,lmar);
  420. << superprinm(x,lmar); terpri(); x >>;
  421. % From here down the functions are not intended for direct use.
  422. symbolic procedure superprinm(x,lmar);
  423. begin
  424. scalar stack,bufferi,buffero,bn,initialblanks,rmar,
  425. pendingrpars,indentlevel,indblanks,rparcount,w;
  426. bufferi:=buffero:=list nil; %fifo buffer.
  427. initialblanks:=0;
  428. rparcount:=0;
  429. indblanks:=0;
  430. rmar:=linelength(nil); % right margin.
  431. linelength 500; % To try to be extra cautious
  432. if rmar<25 then error(0,list(rmar,
  433. "Linelength too short for superprinting"));
  434. bn:=0; %characters in buffer.
  435. indentlevel:=0; %no indentation needed, yet.
  436. if lmar+20>=rmar then lmar:=rmar - 21; %no room for specified margin
  437. w:=posn();
  438. if w>lmar then << terpri(); w:=0 >>;
  439. if w<lmar then initialblanks:=lmar - w;
  440. s!:prindent(x,lmar+3); %main recursive print routine.
  441. % traverse routine finished - now tidy up buffers.
  442. s!:overflow 'none; %flush out the buffer.
  443. linelength rmar;
  444. return x
  445. end;
  446. % Access functions for a stack entry.
  447. smacro procedure s!:top; car stack;
  448. smacro procedure s!:depth frm; car frm;
  449. smacro procedure s!:indenting frm; cadr frm;
  450. smacro procedure s!:blankcount frm; caddr frm;
  451. smacro procedure s!:blanklist frm; cdddr frm;
  452. smacro procedure s!:setindenting(frm,val); rplaca(cdr frm,val);
  453. smacro procedure s!:setblankcount(frm,val); rplaca(cddr frm,val);
  454. smacro procedure s!:setblanklist(frm,val); rplacd(cddr frm,val);
  455. smacro procedure s!:newframe n; list(n,nil,0);
  456. smacro procedure s!:blankp char; numberp car char;
  457. symbolic procedure s!:prindent(x,n);
  458. % Print list x with indentation level n.
  459. if atom x then if simple!-vector!-p x then s!:prvector(x,n)
  460. else for each c in
  461. (if !*pretty!-symmetric
  462. then if stringp x then s!:explodes x else explode x
  463. else explode2 x) do s!:putch c
  464. else if s!:quotep x then <<
  465. s!:putch '!';
  466. s!:prindent(cadr x,n+1) >>
  467. else begin
  468. scalar cx;
  469. if 4*n>3*rmar then << %list is too deep for sanity.
  470. s!:overflow 'all;
  471. n:=truncate(n, 8);
  472. if initialblanks>n then <<
  473. lmar:=lmar - initialblanks+n;
  474. initialblanks:=n >> >>;
  475. stack := (s!:newframe n) . stack;
  476. s!:putch ('lpar . s!:top());
  477. cx:=car x;
  478. s!:prindent(cx,n+1);
  479. if idp cx and not atom cdr x then
  480. cx:=get(cx,'s!:ppformat) else cx:=nil;
  481. if cx=2 and atom cddr x then cx:=nil;
  482. if cx='prog then <<
  483. s!:putch '! ;
  484. s!:prindent(car (x:=cdr x),n+3) >>;
  485. % CX now controls the formatting of what follows:
  486. % nil default action
  487. % <number> first few blanks are non-indenting
  488. % prog display atoms as labels.
  489. x:=cdr x;
  490. scan: if atom x then go to outt;
  491. s!:finishpending(); %about to print a blank.
  492. if cx='prog then <<
  493. s!:putblank();
  494. s!:overflow bufferi; %force format for prog.
  495. if atom car x then << % a label.
  496. lmar:=initialblanks:=max(lmar - 6,0);
  497. s!:prindent(car x,n - 3); % print the label.
  498. x:=cdr x;
  499. if not atom x and atom car x then go to scan;
  500. if lmar+bn>n then s!:putblank()
  501. else for i:=lmar+bn:n - 1 do s!:putch '! ;
  502. if atom x then go to outt>> >>
  503. else if numberp cx then <<
  504. cx:=cx - 1;
  505. if cx=0 then cx:=nil;
  506. s!:putch '! >>
  507. else s!:putblank();
  508. s!:prindent(car x,n+3);
  509. x:=cdr x;
  510. go to scan;
  511. outt: if not null x then <<
  512. s!:finishpending();
  513. s!:putblank();
  514. s!:putch '!.;
  515. s!:putch '! ;
  516. s!:prindent(x,n+5) >>;
  517. s!:putch ('rpar . (n - 3));
  518. if s!:indenting s!:top()='indent and not null s!:blanklist s!:top() then
  519. s!:overflow car s!:blanklist s!:top()
  520. else s!:endlist s!:top();
  521. stack:=cdr stack
  522. end;
  523. symbolic procedure s!:explodes x;
  524. %dummy function just in case another format is needed.
  525. explode x;
  526. symbolic procedure s!:prvector(x,n);
  527. begin
  528. scalar bound;
  529. bound:=upbv x; % length of the vector.
  530. stack:=(s!:newframe n) . stack;
  531. s!:putch ('lsquare . s!:top());
  532. s!:prindent(getv(x,0),n+3);
  533. for i:=1:bound do <<
  534. s!:putch '!,;
  535. s!:putblank();
  536. s!:prindent(getv(x,i),n+3) >>;
  537. s!:putch('rsquare . (n - 3));
  538. s!:endlist s!:top();
  539. stack:=cdr stack
  540. end;
  541. symbolic procedure s!:putblank();
  542. begin
  543. s!:putch s!:top(); %represents a blank character.
  544. s!:setblankcount(s!:top(),s!:blankcount s!:top()+1);
  545. s!:setblanklist(s!:top(),bufferi . s!:blanklist s!:top());
  546. %remember where I was.
  547. indblanks:=indblanks+1
  548. end;
  549. symbolic procedure s!:endlist l;
  550. %Fix up the blanks in a complete list so that they
  551. %will not be turned into indentations.
  552. pendingrpars:=l . pendingrpars;
  553. % When I have printed a ')' I want to mark all of the blanks
  554. % within the parentheses as being unindented, ordinary blank
  555. % characters. It is however possible that I may get a buffer
  556. % overflow while printing a string of )))))))))), and so this
  557. % marking should be delayed until I get round to printing
  558. % a further blank (which will be a candidate for a place to
  559. % split lines). This delay is dealt with by the list
  560. % pendingrpars which holds a list of levels that, when
  561. % convenient, can be tidied up and closed out.
  562. symbolic procedure s!:finishpending();
  563. << for each stackframe in pendingrpars do <<
  564. if s!:indenting stackframe neq 'indent then
  565. for each b in s!:blanklist stackframe do
  566. << rplaca(b,'! ); indblanks:=indblanks - 1>>;
  567. % s!:blanklist of stackframe must be non-nil so that overflow
  568. % will not treat the '(' specially.
  569. s!:setblanklist(stackframe,t) >>;
  570. pendingrpars:=nil >>;
  571. symbolic procedure s!:quotep x;
  572. !*quotes and
  573. not atom x and
  574. car x='quote and
  575. not atom cdr x and
  576. null cddr x;
  577. % property s!:ppformat drives the prettyprinter -
  578. % prog : special for prog only
  579. % 1 : (fn a1
  580. % a2
  581. % ... )
  582. % 2 : (fn a1 a2
  583. % a3
  584. % ... ) ;
  585. put('prog,'s!:ppformat,'prog);
  586. put('lambda,'s!:ppformat,1);
  587. put('lambdaq,'s!:ppformat,1);
  588. put('setq,'s!:ppformat,1);
  589. put('set,'s!:ppformat,1);
  590. put('while,'s!:ppformat,1);
  591. put('t,'s!:ppformat,1);
  592. put('de,'s!:ppformat,2);
  593. put('df,'s!:ppformat,2);
  594. put('dm,'s!:ppformat,2);
  595. put('defun,'s!:ppformat,2);
  596. put('defmacro,'s!:ppformat,2);
  597. put('foreach,'s!:ppformat,4); % (foreach x in y do ...) etc.
  598. % Now for the routines that buffer things on a character by character
  599. % basis, and deal with buffer overflow.
  600. symbolic procedure s!:putch c;
  601. begin
  602. if atom c then rparcount:=0
  603. else if s!:blankp c then << rparcount:=0; go to nocheck >>
  604. else if car c='rpar then <<
  605. rparcount:=rparcount+1;
  606. % format for a long string of rpars is:
  607. % )))) ))) ))) ))) ))) ;
  608. if rparcount>4 then << s!:putch '! ; rparcount:=2 >> >>
  609. else rparcount:=0;
  610. while lmar+bn>=rmar do s!:overflow 'more;
  611. nocheck:
  612. bufferi:=cdr rplacd(bufferi,list c);
  613. bn:=bn+1
  614. end;
  615. symbolic procedure s!:overflow flg;
  616. begin
  617. scalar c,blankstoskip;
  618. % The current buffer holds so much information that it will
  619. % not all fit on a line. try to do something about it.
  620. % flg is one of:
  621. % 'none do not force more indentation
  622. % 'more force one level more indentation
  623. % <a pointer into the buffer>
  624. % prints up to and including that character, which
  625. % should be a blank.
  626. if indblanks=0 and initialblanks>3 and flg='more then <<
  627. initialblanks:=initialblanks - 3;
  628. lmar:=lmar - 3;
  629. return 'moved!-left >>;
  630. fblank:
  631. if bn=0 then <<
  632. % No blank found - can do no more for now.
  633. % If flg='more I am in trouble and so have to print
  634. % a continuation mark. in the other cases I can just exit.
  635. if not(flg = 'more) then return 'empty;
  636. if atom car buffero then
  637. % continuation mark not needed if last char printed was
  638. % special (e.g. lpar or rpar).
  639. prin2 "%+"; %continuation marker.
  640. terpri();
  641. lmar:=0;
  642. return 'continued >>
  643. else <<
  644. spaces initialblanks;
  645. initialblanks:=0 >>;
  646. buffero:=cdr buffero;
  647. bn:=bn - 1;
  648. lmar:=lmar+1;
  649. c:=car buffero;
  650. if atom c then <<
  651. prin2 c;
  652. go to fblank >>
  653. else if s!:blankp c then if not atom blankstoskip then <<
  654. prin2 '! ;
  655. indblanks:=indblanks - 1;
  656. % blankstoskip = (stack-frame . skip-count).
  657. if c eq car blankstoskip then <<
  658. rplacd(blankstoskip,cdr blankstoskip - 1);
  659. if cdr blankstoskip=0 then blankstoskip:=t >>;
  660. go to fblank >>
  661. else go to blankfound
  662. else if car c='lpar or car c='lsquare then <<
  663. prin2 get(car c,'s!:ppchar);
  664. if flg='none then go to fblank;
  665. % now I want to flag this level for indentation.
  666. c:=cdr c; %the stack frame.
  667. if not null s!:blanklist c then go to fblank;
  668. if s!:depth c>indentlevel then << %new indentation.
  669. % this level has not emitted any blanks yet.
  670. indentlevel:=s!:depth c;
  671. s!:setindenting(c,'indent) >>;
  672. go to fblank >>
  673. else if car c='rpar or car c='rsquare then <<
  674. if cdr c<indentlevel then indentlevel:=cdr c;
  675. prin2 get(car c,'s!:ppchar);
  676. go to fblank >>
  677. else error(0,list(c,"UNKNOWN TAG IN OVERFLOW"));
  678. blankfound:
  679. if eqcar(s!:blanklist c,buffero) then
  680. s!:setblanklist(c,nil);
  681. % at least one entry on blanklist ought to be valid, so if I
  682. % print the last blank I must kill blanklist totally.
  683. indblanks:=indblanks - 1;
  684. % check if next level represents new indentation.
  685. if s!:depth c>indentlevel then <<
  686. if flg='none then << %just print an ordinary blank.
  687. prin2 '! ;
  688. go to fblank >>;
  689. % here I increase the indentation level by one.
  690. if blankstoskip then blankstoskip:=nil
  691. else <<
  692. indentlevel:=s!:depth c;
  693. s!:setindenting(c,'indent) >> >>;
  694. %otherwise I was indenting at that level anyway.
  695. if s!:blankcount c>(thin!* - 1) then << %long thin list fix-up here.
  696. blankstoskip:=c . ((s!:blankcount c) - 2);
  697. s!:setindenting(c,'thin);
  698. s!:setblankcount(c,1);
  699. indentlevel:=(s!:depth c) - 1;
  700. prin2 '! ;
  701. go to fblank >>;
  702. s!:setblankcount(c,(s!:blankcount c) - 1);
  703. terpri();
  704. lmar:=initialblanks:=s!:depth c;
  705. if buffero eq flg then return 'to!-flg;
  706. if blankstoskip or not (flg='more) then go to fblank;
  707. % keep going unless call was of type 'more'.
  708. return 'more; %try some more.
  709. end;
  710. put('lpar,'s!:ppchar,'!();
  711. put('lsquare,'s!:ppchar,'![);
  712. put('rpar,'s!:ppchar,'!));
  713. put('rsquare,'s!:ppchar,'!]);
  714. % Now some (experimental) support for network access
  715. symbolic procedure fetch!-url(url, !&optional, dest);
  716. begin
  717. scalar a, b, c, d, e, w;
  718. a := open!-url url;
  719. if null a then return nil;
  720. if dest then <<
  721. d := open(dest, 'output);
  722. if null d then <<
  723. close a;
  724. return error(0, "unable to open destination file") >>;
  725. d := wrs d >>;
  726. b := rds a;
  727. w := linelength 500;
  728. while not ((c := readch()) = !$eof!$) do princ c;
  729. linelength e;
  730. rds b;
  731. close a;
  732. if dest then close wrs d
  733. end;
  734. end;