% This package prints list structures in an indented format that
% is intended to make them legible. There are a number of special
% cases recognized, but in general the intent of the algorithm
% is that given a list (R1 R2 R3 ...), SUPERPRINT checks if
% the list will fit directly on the current line and if so
% prints it as:
%        (R1 R2 R3 ...)
% if not it prints it as:
%        (R1
%           R2
%           R3
%           ... )
% where each sublist is similarly treated.
%
%                       A. C. Norman.  July 1978;


% Functions:
%   SUPERPRINT(X)      print expression X
%   SUPERPRINTM(X,M)   print expression X with left margin M
%   PRETTYPRINT(X)     = << SUPERPRINTM(X,POSN()), TERPRI() >>
%
% Flag:
%   !*SYMMETRIC        If TRUE, print with escape characters,
%                      otherwise do not (as PRIN1/PRIN2
%                      distinction). defaults to TRUE;
%   !*QUOTES           If TRUE, (QUOTE x) gets displayed as 'x.
%                      default is TRUE;
%
% Variable:
%   THIN!*             if THIN!* expressions can be fitted onto
%                      a single line they will be printed that way.
%                      this is a parameter used to control the
%                      formatting of long thin lists. default 
%                      value is 5;



SYMBOLIC;

GLOBAL '(!*SYMMETRIC !*QUOTES THIN!*);

!*SYMMETRIC:=T;
!*QUOTES:=T;
THIN!*:=5;

SYMBOLIC PROCEDURE SUPERPRINT X;
 << SUPERPRINM(X,0); TERPRI(); X>>;

SYMBOLIC PROCEDURE PRETTYPRINT X;
 << SUPERPRINM(X,POSN()); %WHAT REDUCE DOES NOW;
    TERPRI();
    TERPRI();
    NIL>>;

SYMBOLIC PROCEDURE SUPERPRINTM(X,LMAR);
  << SUPERPRINM(X,LMAR); TERPRI(); X >>;


% FROM HERE DOWN THE FUNCTIONS ARE NOT INTENDED FOR DIRECT USE;

% THE FOLLOWING FUNCTIONS ARE DEFINED HERE IN CASE THIS PACKAGE
% IS CALLED FROM LISP RATHER THAN REDUCE;

SYMBOLIC PROCEDURE EQCAR(A,B);
    PAIRP A AND CAR A EQ B;

SYMBOLIC PROCEDURE SPACES N;
    FOR I=1:N DO PRIN2 '! ;

% END OF COMPATIBILITY SECTION;

FLUID '(STACK BUFFERI BUFFERO BN LMAR RMAR INITIALBLANKS
        PENDINGRPARS INDENTLEVEL INDBLANKS RPARCOUNT);

SYMBOLIC PROCEDURE SUPERPRINM(X,LMAR);
  BEGIN
    SCALAR STACK,BUFFERI,BUFFERO,BN,INITIALBLANKS,RMAR,
           PENDINGRPARS,INDENTLEVEL,INDBLANKS,RPARCOUNT,W;
    BUFFERI:=BUFFERO:=LIST NIL; %FIFO BUFFER;
    INITIALBLANKS:=0;
    RPARCOUNT:=0;
    INDBLANKS:=0;
    RMAR:=LINELENGTH(NIL)-3; %RIGHT MARGIN;
    IF RMAR<25 THEN ERROR(0,LIST(RMAR+3,
        "LINELENGTH TOO SHORT FOR SUPERPRINTING"));
    BN:=0; %CHARACTERS IN BUFFER;
    INDENTLEVEL:=0; %NO INDENTATION NEEDED, YET;
    IF LMAR+20>=RMAR THEN LMAR:=RMAR-21; %NO ROOM FOR SPECIFIED MARGIN;
    W:=POSN();
    IF W>LMAR THEN << TERPRI(); W:=0 >>;
    IF W<LMAR THEN INITIALBLANKS:=LMAR-W;
    PRINDENT(X,LMAR+3); %MAIN RECURSIVE PRINT ROUTINE;
% TRAVERSE ROUTINE FINISHED - NOW TIDY UP BUFFERS;
    OVERFLOW 'NONE; %FLUSH OUT THE BUFFER;
    RETURN X
  END;


% ACCESS FUNCTIONS FOR A STACK ENTRY;


SMACRO PROCEDURE TOP; CAR STACK;
SMACRO PROCEDURE DEPTH FRM; CAR FRM;
SMACRO PROCEDURE INDENTING FRM; CADR FRM;
SMACRO PROCEDURE BLANKCOUNT FRM; CADDR FRM;
SMACRO PROCEDURE BLANKLIST FRM; CDDDR FRM;
SMACRO PROCEDURE SETINDENTING(FRM,VAL); RPLACA(CDR FRM,VAL);
SMACRO PROCEDURE SETBLANKCOUNT(FRM,VAL); RPLACA(CDDR FRM,VAL);
SMACRO PROCEDURE SETBLANKLIST(FRM,VAL); RPLACD(CDDR FRM,VAL);
SMACRO PROCEDURE NEWFRAME N; LIST(N,NIL,0);
SMACRO PROCEDURE BLANKP CHAR; NUMBERP CAR CHAR;





SYMBOLIC PROCEDURE PRINDENT(X,N);
% PRINT LIST X WITH INDENTATION LEVEL N;
    IF ATOM X THEN IF VECTORP X THEN PRVECTOR(X,N)
        ELSE FOR EACH C IN 
	  (IF !*SYMMETRIC
	     THEN IF STRINGP X THEN EXPLODES X ELSE EXPLODE X
            ELSE EXPLODEC X) DO PUTCH C
    ELSE IF QUOTEP X THEN <<
        PUTCH '!';
        PRINDENT(CADR X,N+1) >>
    ELSE BEGIN
        SCALAR CX;
        IF 4*N>3*RMAR THEN << %LIST IS TOO DEEP FOR SANITY;
            OVERFLOW 'ALL;
            N:=N/8;
            IF INITIALBLANKS>N THEN <<
                LMAR:=LMAR-INITIALBLANKS+N;
                INITIALBLANKS:=N >> >>;
        STACK := (NEWFRAME N) . STACK;
        PUTCH ('LPAR . TOP());
        CX:=CAR X;
        PRINDENT(CX,N+1);
        IF IDP CX AND NOT ATOM CDR X THEN 
            CX:=GET(CX,'PPFORMAT) ELSE CX:=NIL;
        IF CX=2 AND ATOM CDDR X THEN CX:=NIL;
        IF CX='PROG THEN <<
            PUTCH '! ;
            PRINDENT(CAR (X:=CDR X),N+3) >>;
% CX NOW CONTROLS THE FORMATTING OF WHAT FOLLOWS:
%    NIL      DEFAULT ACTION
%    <NUMBER> FIRST FEW BLANKS ARE NON-INDENTING
%    PROG     DISPLAY ATOMS AS LABELS;
         X:=CDR X;

   SCAN: IF ATOM X THEN GO TO OUT;
         FINISHPENDING(); %ABOUT TO PRINT A BLANK;
         IF CX='PROG THEN <<
             PUTBLANK();
             OVERFLOW BUFFERI; %FORCE FORMAT FOR PROG;
             IF ATOM CAR X THEN << % A LABEL;
                 LMAR:=INITIALBLANKS:=MAX(LMAR-6,0);
                 PRINDENT(CAR X,N-3); % PRINT THE LABEL;
                 X:=CDR X;
                 IF NOT ATOM X AND ATOM CAR X THEN GO TO SCAN;
                 IF LMAR+BN>N THEN PUTBLANK()
                 ELSE FOR I=LMAR+BN:N-1 DO PUTCH '! ;
                 IF ATOM X THEN GO TO OUT >> >>
         ELSE IF NUMBERP CX THEN <<
             CX:=CX-1;
             IF CX=0 THEN CX:=NIL;
             PUTCH '!  >>
         ELSE PUTBLANK();
         PRINDENT(CAR X,N+3);
         X:=CDR X;
         GO TO SCAN;

   OUT:  IF NOT NULL X THEN <<
            FINISHPENDING();
            PUTBLANK();
            PUTCH '!.;
            PUTCH '! ;
            PRINDENT(X,N+5) >>;
        PUTCH ('RPAR . (N-3));
        IF INDENTING TOP()='INDENT AND NOT NULL BLANKLIST TOP() THEN
               OVERFLOW CAR BLANKLIST TOP()
        ELSE ENDLIST TOP();
        STACK:=CDR STACK
      END;

SYMBOLIC PROCEDURE EXPLODES X;
   %dummy function just in case another format is needed;
   EXPLODE X;

SYMBOLIC PROCEDURE PRVECTOR(X,N);
  BEGIN
    SCALAR BOUND;
    BOUND:=UPBV X; % LENGTH OF THE VECTOR;
    STACK:=(NEWFRAME N) . STACK;
    PUTCH ('LSQUARE . TOP());
    PRINDENT(GETV(X,0),N+3);
    FOR I=1:BOUND DO <<
        PUTCH '!,;
        PUTBLANK();
        PRINDENT(GETV(X,I),N+3) >>;
    PUTCH('RSQUARE . (N-3));
    ENDLIST TOP();
    STACK:=CDR STACK
  END;

SYMBOLIC PROCEDURE PUTBLANK();
  BEGIN
    SCALAR B;
    PUTCH TOP(); %REPRESENTS A BLANK CHARACTER;
    SETBLANKCOUNT(TOP(),BLANKCOUNT TOP()+1);
    SETBLANKLIST(TOP(),BUFFERI . BLANKLIST TOP());
	 %REMEMBER WHERE I WAS;
    INDBLANKS:=INDBLANKS+1
  END;




SYMBOLIC PROCEDURE ENDLIST L;
%FIX UP THE BLANKS IN A COMPLETE LIST SO THAT THEY
%WILL NOT BE TURNED INTO INDENTATIONS;
     PENDINGRPARS:=L . PENDINGRPARS;

% WHEN I HAVE PRINTED A ')' I WANT TO MARK ALL OF THE BLANKS
% WITHIN THE PARENTHESES AS BEING UNINDENTED, ORDINARY BLANK
% CHARACTERS. IT IS HOWEVER POSSIBLE THAT I MAY GET A BUFFER
% OVERFLOW WHILE PRINTING A STRING OF )))))))))), AND SO THIS
% MARKING SHOULD BE DELAYED UNTIL I GET ROUND TO PRINTING
% A FURTHER BLANK (WHICH WILL BE A CANDIDATE FOR A PLACE TO
% SPLIT LINES). THIS DELAY IS DEALT WITH BY THE LIST
% PENDINGRPARS WHICH HOLDS A LIST OF LEVELS THAT, WHEN
% CONVENIENT, CAN BE TIDIED UP AND CLOSED OUT;

SYMBOLIC PROCEDURE FINISHPENDING();
 << FOR EACH STACKFRAME IN PENDINGRPARS DO <<
        IF INDENTING STACKFRAME NEQ 'INDENT THEN
            FOR EACH B IN BLANKLIST STACKFRAME DO
              << RPLACA(B,'! ); INDBLANKS:=INDBLANKS-1 >>;
% BLANKLIST OF STACKFRAME MUST BE NON-NIL SO THAT OVERFLOW
% WILL NOT TREAT THE '(' SPECIALLY;
        SETBLANKLIST(STACKFRAME,T) >>;
    PENDINGRPARS:=NIL >>;



SYMBOLIC PROCEDURE QUOTEP X;
    !*QUOTES AND
    NOT ATOM X AND
    CAR X='QUOTE AND
    NOT ATOM CDR X AND
    NULL CDDR X;






% PROPERTY PPFORMAT DRIVES THE PRETTYPRINTER -
% PROG     : SPECIAL FOR PROG ONLY
% 1        :    (FN A1
%                  A2
%                  ... )
% 2        :    (FN A1 A2
%                  A3
%                  ... )     ;

PUT('PROG,'PPFORMAT,'PROG);
PUT('LAMBDA,'PPFORMAT,1);
PUT('LAMBDAQ,'PPFORMAT,1);
PUT('SETQ,'PPFORMAT,1);
PUT('SET,'PPFORMAT,1);
PUT('WHILE,'PPFORMAT,1);
PUT('T,'PPFORMAT,1);
PUT('DE,'PPFORMAT,2);
PUT('DF,'PPFORMAT,2);
PUT('DM,'PPFORMAT,2);
PUT('FOREACH,'PPFORMAT,4); % (FOREACH X IN Y DO ...) ETC;


% NOW FOR THE ROUTINES THAT BUFFER THINGS ON A CHARACTER BY CHARACTER
% BASIS, AND DEAL WITH BUFFER OVERFLOW;


SYMBOLIC PROCEDURE PUTCH C;
  BEGIN
    IF ATOM C THEN RPARCOUNT:=0
    ELSE IF BLANKP C THEN << RPARCOUNT:=0; GO TO NOCHECK >>
    ELSE IF CAR C='RPAR THEN <<
        RPARCOUNT:=RPARCOUNT+1;
% FORMAT FOR A LONG STRING OF RPARS IS:
%    )))) ))) ))) ))) )))   ;
        IF RPARCOUNT>4 THEN << PUTCH '! ; RPARCOUNT:=2 >> >>
    ELSE RPARCOUNT:=0;
    WHILE LMAR+BN>=RMAR DO OVERFLOW 'MORE;
NOCHECK:
    BUFFERI:=CDR RPLACD(BUFFERI,LIST C);
    BN:=BN+1 
  END;

SYMBOLIC PROCEDURE OVERFLOW FLG;
  BEGIN
    SCALAR C,BLANKSTOSKIP;
%THE CURRENT BUFFER HOLDS SO MUCH INFORMATION THAT IT WILL
%NOT ALL FIT ON A LINE. TRY TO DO SOMETHING ABOUT IT;
% FLG IS ONE OF:
%  'NONE       DO NOT FORCE MORE INDENTATION
%  'MORE       FORCE ONE LEVEL MORE INDENTATION
% <A POINTER INTO THE BUFFER>
%               PRINTS UP TO AND INCLUDING THAT CHARACTER, WHICH
%               SHOULD BE A BLANK;
    IF INDBLANKS=0 AND INITIALBLANKS>3 AND FLG='MORE THEN <<
        INITIALBLANKS:=INITIALBLANKS-3;
        LMAR:=LMAR-3;
        RETURN 'MOVED!-LEFT >>;
FBLANK:
    IF BN=0 THEN <<
%NO BLANK FOUND - CAN DO NO MORE FOR NOW;
% IF FLG='MORE I AM IN TROUBLE AND SO HAVE TO PRINT
% A CONTINUATION MARK. IN THE OTHER CASES I CAN JUST EXIT;
        IF NOT(FLG = 'MORE) THEN RETURN 'EMPTY;
        IF ATOM CAR BUFFERO THEN
% CONTINUATION MARK NOT NEEDED IF LAST CHAR PRINTED WAS
% SPECIAL (E.G. LPAR OR RPAR);
            PRIN2 "%+"; %CONTINUATION MARKER;
        TERPRI();
        LMAR:=0;
        RETURN 'CONTINUED >>
    ELSE <<
        SPACES INITIALBLANKS;
        INITIALBLANKS:=0 >>;
    BUFFERO:=CDR BUFFERO;
    BN:=BN-1;
    LMAR:=LMAR+1;
    C:=CAR BUFFERO;
    IF ATOM C THEN << PRIN2 C; GO TO FBLANK >>
    ELSE IF BLANKP C THEN IF NOT ATOM BLANKSTOSKIP THEN <<
	PRIN2 '! ;
        INDBLANKS:=INDBLANKS-1;
% BLANKSTOSKIP = (STACK-FRAME . SKIP-COUNT);
        IF C EQ CAR BLANKSTOSKIP THEN <<
            RPLACD(BLANKSTOSKIP,CDR BLANKSTOSKIP-1);
            IF CDR BLANKSTOSKIP=0 THEN BLANKSTOSKIP:=T >>;
        GO TO FBLANK >>
      ELSE GO TO BLANKFOUND
    ELSE IF CAR C='LPAR OR CAR C='LSQUARE THEN <<
	PRIN2 GET(CAR C,'PPCHAR);
        IF FLG='NONE THEN GO TO FBLANK;
% NOW I WANT TO FLAG THIS LEVEL FOR INDENTATION;
        C:=CDR C; %THE STACK FRAME;
        IF NOT NULL BLANKLIST C THEN GO TO FBLANK;
        IF DEPTH C>INDENTLEVEL THEN << %NEW INDENTATION;
% THIS LEVEL HAS NOT EMITTED ANY BLANKS YET;
            INDENTLEVEL:=DEPTH C;
            SETINDENTING(C,'INDENT) >>;
        GO TO FBLANK >>
    ELSE IF CAR C='RPAR OR CAR C='RSQUARE THEN <<
        IF CDR C<INDENTLEVEL THEN INDENTLEVEL:=CDR C;
	PRIN2 GET(CAR C,'PPCHAR);
        GO TO FBLANK >>
    ELSE ERROR(0,LIST(C,"UNKNOWN TAG IN OVERFLOW"));

BLANKFOUND:
    IF EQCAR(BLANKLIST C,BUFFERO) THEN
        SETBLANKLIST(C,NIL);
% AT LEAST ONE ENTRY ON BLANKLIST OUGHT TO BE VALID, SO IF I
% PRINT THE LAST BLANK I MUST KILL BLANKLIST TOTALLY;
    INDBLANKS:=INDBLANKS-1;
% CHECK IF NEXT LEVEL REPRESENTS NEW INDENTATION;
    IF DEPTH C>INDENTLEVEL THEN <<
        IF FLG='NONE THEN << %JUST PRINT AN ORDINARY BLANK;
	    PRIN2 '! ;
            GO TO FBLANK >>;
% HERE I INCREASE THE INDENTATION LEVEL BY ONE;
        IF BLANKSTOSKIP THEN BLANKSTOSKIP:=NIL
        ELSE <<
            INDENTLEVEL:=DEPTH C;
            SETINDENTING(C,'INDENT) >> >>;
%OTHERWISE I WAS INDENTING AT THAT LEVEL ANYWAY;
    IF BLANKCOUNT C>(THIN!*-1) THEN << %LONG THIN LIST FIX-UP HERE;
        BLANKSTOSKIP:=C . ((BLANKCOUNT C) - 2);
        SETINDENTING(C,'THIN);
        SETBLANKCOUNT(C,1);
        INDENTLEVEL:=(DEPTH C)-1;
	PRIN2 '! ;
        GO TO FBLANK >>;
    SETBLANKCOUNT(C,(BLANKCOUNT C)-1);
    TERPRI();
    LMAR:=INITIALBLANKS:=DEPTH C;
    IF BUFFERO EQ FLG THEN RETURN 'TO!-FLG;
    IF BLANKSTOSKIP OR NOT (FLG='MORE) THEN GO TO FBLANK;
% KEEP GOING UNLESS CALL WAS OF TYPE 'MORE';
    RETURN 'MORE; %TRY SOME MORE;
  END;

PUT('LPAR,'PPCHAR,'!();
PUT('LSQUARE,'PPCHAR,'![);
PUT('RPAR,'PPCHAR,'!));
PUT('RSQUARE,'PPCHAR,'!]);

END;
