%
% PRINTERS.RED - Printing functions for various data types
% 
% Author:      Eric Benson
%	       Symbolic Computation Group
%              Computer Science Dept.
%              University of Utah
% Date:        27 August 1981
% Copyright (c) 1981 University of Utah
%
%  <PSL.KERNEL>PRINTERS.RED.17,  7-Mar-83 11:53:59, Edit by KESSLER
%  Change Channelwriteblankoreol to check linelength = 0 also.
%  03-Mar-83  Nancy Kendzierski
%  Changed declaration of LispScanTable!* from global to fluid.
% Edit by MLGriss, 11:31am  Saturday, 5 February 1983
%   Fix ChannelWriteBitstring to put out a single 0 if needed
%   Fixed to handle largest NEGATIVE number correctly
%   Used to get ------, since -(largest neg) NOT=largestPOS
% <PSL.KERNEL>PRINTERS.RED.14, 31-Jan-83 15:45:30, Edit by PERDUE
% Fix to printing of EVECTORs
% Edit by Cris Perdue, 29 Jan 1983 1620-PST
% Removed definition of EVecInf (both compile- and load-time)
% Edit by Cris Perdue, 27 Jan 1983 1436-PST
% Put in Kessler's change so CheckLineFit won't write EOL if LineLength = 0
%  <PSL.KERNEL>PRINTERS.RED.11, 10-Jan-83 13:58:14, Edit by PERDUE
%  Added some code to handle EVectors, especially to represent OBJECTs
%  <PSL.KERNEL>PRINTERS.RED.10, 21-Dec-82 15:24:18, Edit by BENSON
%  Changed order of tests in WriteInteger so that -ive hex #s are done right
%  <PSL.KERNEL>PRINTERS.RED.9,  4-Oct-82 10:04:34, Edit by BENSON
%  Added PrinLength and PrinLevel
%  <PSL.KERNEL>PRINTERS.RED.3, 23-Sep-82 13:16:20, Edit by BENSON
%  Look for # of args in code pointer, changed : to space in #<...> stuff
%  <PSL.INTERP>PRINTERS.RED.12,  2-Sep-82 09:01:31, Edit by BENSON
%  (QUOTE x y) prints correctly, not as 'x
%  <PSL.INTERP>PRINTERS.RED.11,  4-May-82 20:31:32, Edit by BENSON
%  Printers keep tags on, for Emode GC
%  <PSL.VAX-INTERP>PRINTERS.RED.6, 18-Feb-82 16:30:12, Edit by BENSON
%  Added printer for unbound, changed code to #<Code:xx>
%  <PSL.VAX-INTERP>PRINTERS.RED.2, 20-Jan-82 02:11:16, Edit by GRISS
%  fixed prining of zero length vectors
%  <PSL.VAX-INTERP>PRINTERS.RED.1, 15-Jan-82 14:27:13, Edit by BENSON
%  Changed for new integer tags
%  <PSL.INTERP>PRINTERS.RED.13,  7-Jan-82 22:47:40, Edit by BENSON
%  Made (QUOTE xxx) print as 'xxx
%  <PSL.INTERP>PRINTERS.RED.12,  5-Jan-82 21:37:41, Edit by BENSON
%  Changed OBase to OutputBase!*

fluid '(OutputBase!*			% current output base
        PrinLength			% length of structures to print
	PrinLevel			% level of recursion to print
	CurrentScanTable!*
	LispScanTable!*
	IDEscapeChar!*
	!*Lower);		% print IDs with uppercase chars lowered

LoadTime
<<  OutputBase!* := 10;
    IDEscapeChar!* := 33;		% (char !!)
    CurrentScanTable!* := LispScanTable!* >>; % so TokenTypeOfChar works right

on SysLisp;

CompileTime <<
syslsp smacro procedure UpperCaseP Ch;
    Ch >= char A and Ch <= char Z;

syslsp smacro procedure LowerCaseP Ch;
    Ch >= char !a and Ch <= char !z;

syslsp smacro procedure RaiseChar Ch;
    (Ch - char !a) + char A;

syslsp smacro procedure LowerChar Ch;
    (Ch - char A) + char !a;
>>;

CompileTime flag('(CheckLineFit WriteNumber1 ChannelWriteBitString),
		 'InternalFunction);

%. Writes EOL first if given Len causes max line length to be exceeded
syslsp procedure CheckLineFit(Len, Chn, Fn, Itm);
<<  if (LinePosition[Chn] + Len > MaxLine[Chn]) and (MaxLine[Chn] > 0) then
	ChannelWriteChar(Chn, char EOL);
    IDApply2(Chn, Itm, Fn) >>;

syslsp procedure ChannelWriteString(Channel, Strng);
%
% Strng may be tagged or not, but it must have a length field accesible
% by StrLen.
%
begin scalar UpLim;
    UpLim := StrLen StrInf Strng;
    for I := 0 step 1 until UpLim do
	ChannelWriteChar(Channel, StrByt(StrInf Strng, I));
end;

syslsp procedure WriteString S;
    ChannelWriteString(LispVar OUT!*, S);

internal WString DigitString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
internal WString WriteNumberBuffer[40];

syslsp procedure ChannelWriteSysInteger(Channel, Number, Radix);
begin scalar Exponent,N1;
    return if (Exponent := SysPowerOf2P Radix) then
	ChannelWriteBitString(Channel, Number, Radix - 1, Exponent)
    else if Number < 0 then
    <<  ChannelWriteChar(Channel, char '!-);
        WriteNumber1(Channel,-(Number/Radix),Radix); % To catch largest NEG
	ChannelWriteChar(Channel,
			 StrByt(DigitString, - MOD(Number, Radix))) >>
    else if Number = 0 then ChannelWriteChar(Channel, char !0)
    else WriteNumber1(Channel, Number, Radix);
end;

syslsp procedure WriteNumber1(Channel, Number, Radix);
    if Number = 0 then Channel
    else
    <<  WriteNumber1(Channel, Number / Radix, Radix);
	ChannelWriteChar(Channel,
			 StrByt(DigitString, MOD(Number, Radix))) >>;

syslsp procedure ChannelWriteBitString(Channel, Number, DigitMask, Exponent);
 if Number = 0 then ChannelWriteChar(Channel,char !0)
  else  ChannelWriteBitStrAux(Channel, Number, DigitMask, Exponent);

syslsp procedure ChannelWriteBitStrAux(Channel, Number, DigitMask, Exponent);
    if Number = 0 then Channel		% Channel means nothing here
    else				% just trying to fool the compiler
    <<  ChannelWriteBitStrAux(Channel,
			      LSH(Number, -Exponent),
			      DigitMask,
			      Exponent);
	ChannelWriteChar(Channel,
			 StrByt(DigitString,
				LAND(Number, DigitMask))) >>;

syslsp procedure WriteSysInteger(Number, Radix);
    ChannelWriteSysInteger(LispVar OUT!*, Number, Radix);

syslsp procedure ChannelWriteFixnum(Channel, Num);
    ChannelWriteInteger(Channel, FixVal FixInf Num);

syslsp procedure ChannelWriteInteger(Channel, Num);
begin scalar CurrentBase;
    if (CurrentBase := LispVar OutputBase!*) neq 10 then
    <<  ChannelWriteSysInteger(Channel, CurrentBase, 10);
	ChannelWriteChar(Channel, char !#) >>;
    ChannelWriteSysInteger(Channel,
			   Num,
			   CurrentBase);
end;

syslsp procedure ChannelWriteSysFloat(Channel, FloatPtr);
begin scalar Ch, ChIndex;
    WriteFloat(WriteNumberBuffer, FloatPtr);
    ChannelWriteString(Channel, WriteNumberBuffer);
end;

syslsp procedure ChannelWriteFloat(Channel, LispFloatPtr);
    ChannelWriteSysFloat(Channel, FloatBase FltInf LispFloatPtr);

syslsp procedure ChannelPrintString(Channel, Strng);
begin scalar Len, Ch;
    ChannelWriteChar(Channel, char !");
    Len := StrLen StrInf Strng;
    for I := 0 step 1 until Len do
    <<  Ch := StrByt(StrInf Strng, I);
	if Ch eq char !" then ChannelWriteChar(Channel, char !");
	ChannelWriteChar(Channel, Ch) >>;
    ChannelWriteChar(Channel, char !");
end;

syslsp procedure ChannelWriteID(Channel, Itm);
    if not LispVar !*Lower then
	ChannelWriteString(Channel, SymNam IDInf Itm)
    else begin scalar Ch, Len;
	Itm := StrInf SymNam IDInf Itm;
	Len := StrLen Itm;
	for I := 0 step 1 until Len do
	<<  Ch := StrByt(Itm, I);
	    if UpperCaseP Ch then Ch := LowerChar Ch;
	    ChannelWriteChar(Channel, Ch) >>;
    end;

syslsp procedure ChannelWriteUnbound(Channel, Itm);
<<  ChannelWriteString(Channel, "#<Unbound:");
    ChannelWriteID(Channel, Itm);
    ChannelWriteChar(Channel, char '!>) >>;

syslsp procedure ChannelPrintID(Channel, Itm);
begin scalar Len, Ch, TokenType;
    Itm := StrInf SymNam IDInf Itm;
    Len := StrLen Itm;
    Ch := StrByt(Itm, 0);
    if TokenTypeOfChar Ch neq 10 then ChannelWriteChar(Channel,
						       LispVar IDEscapeChar!*);
    if not LispVar !*Lower then
    <<  ChannelWriteChar(Channel, Ch);
	for I := 1 step 1 until Len do
	<<  Ch := StrByt(Itm, I);
	    TokenType := TokenTypeOfChar Ch;
	    if not (TokenType <= 10
			or TokenType eq PLUSSIGN
			or TokenType eq MINUSSIGN) then
		ChannelWriteChar(Channel, LispVar IDEscapeChar!*);
	    ChannelWriteChar(Channel, Ch) >> >>
    else
    <<  if UpperCaseP Ch then Ch := LowerChar Ch;
	ChannelWriteChar(Channel, Ch);
	for I := 1 step 1 until Len do
	<<  Ch := StrByt(Itm, I);
	    TokenType := TokenTypeOfChar Ch;
	    if not (TokenType <= 10
			or TokenType eq PLUSSIGN
			or TokenType eq MINUSSIGN) then
	        ChannelWriteChar(Channel, LispVar IDEscapeChar!*);
	    if UpperCaseP Ch then Ch := LowerChar Ch;
	    ChannelWriteChar(Channel, Ch) >> >>
end;

syslsp procedure ChannelPrintUnbound(Channel, Itm);
<<  ChannelWriteString(Channel, "#<Unbound ");
    ChannelPrintID(Channel, Itm);
    ChannelWriteChar(Channel, char '!>) >>;

syslsp procedure ChannelWriteCodePointer(Channel, CP);
begin scalar N;
    CP := CodeInf CP;
    ChannelWriteString(Channel, "#<Code ");
    N := !%code!-number!-of!-arguments CP;
    if N >= 0 and N <= MaxArgs then
    <<  ChannelWriteSysInteger(Channel, N, 10);
	ChannelWriteChar(Channel, char BLANK) >>:
    ChannelWriteSysInteger(Channel, CP, CompressedBinaryRadix);
    ChannelWriteChar(Channel, char '!>);
end;

syslsp procedure ChannelWriteUnknownItem(Channel, Itm);
<<  ChannelWriteString(Channel, "#<Unknown ");
    ChannelWriteSysInteger(Channel, Itm, CompressedBinaryRadix);
    ChannelWriteChar(Channel, char !>) >>;

syslsp procedure ChannelWriteBlankOrEOL Channel;
<<  if (LinePosition[Channel] + 1 >= MaxLine[Channel]) and
       (MaxLine[Channel] > 0) then
	ChannelWriteChar(Channel, char EOL)
    else
	ChannelWriteChar(Channel, char ! ) >>;

syslsp procedure ChannelWritePair(Channel, Itm, Level);
    if IntP LispVar PrinLevel and Level >= LispVar PrinLevel then
	ChannelWriteChar(Channel, char '!#)
    else
begin scalar N;
    Level := Level + 1;
    CheckLineFit(1, Channel, 'ChannelWriteChar, char !( );
    if not IntP LispVar PrinLength or 1 <= LispVar PrinLength then
    <<  RecursiveChannelPrin2(Channel, car Itm, Level);
	N := 2;
	Itm := cdr Itm;
	while PairP Itm and
		(not IntP LispVar PrinLength or N <= LispVar PrinLength) do
	<<  ChannelWriteBlankOrEOL Channel;
	    RecursiveChannelPrin2(Channel, car Itm, Level);
	    N := N + 1;
	    Itm := cdr Itm >>;
	if PairP Itm then
	    CheckLineFit(3, Channel, 'ChannelWriteString, " ...")
	else
	if Itm then
	<<  CheckLineFit(3, Channel, 'ChannelWriteString, " . ");
	    RecursiveChannelPrin2(Channel, Itm, Level) >> >>
    else
	CheckLineFit(3, Channel, 'ChannelWriteString, "...");
    CheckLineFit(1, Channel, 'ChannelWriteChar, char !) );
end;

syslsp procedure ChannelPrintPair(Channel, Itm, Level);
    if IntP LispVar PrinLevel and Level >= LispVar PrinLevel then
	ChannelWriteChar(Channel, char '!#)
    else
begin scalar N;
    Level := Level + 1;
    CheckLineFit(1, Channel, 'ChannelWriteChar, char !( );
    if not IntP LispVar PrinLength or 1 <= LispVar PrinLength then
    <<  RecursiveChannelPrin1(Channel, car Itm, Level);
	N := 2;
	Itm := cdr Itm;
	while PairP Itm and
		(not IntP LispVar PrinLength or N <= LispVar PrinLength) do
	<<  ChannelWriteBlankOrEOL Channel;
	    RecursiveChannelPrin1(Channel, car Itm, Level);
	    N := N + 1;
	    Itm := cdr Itm >>;
	if PairP Itm then
	    CheckLineFit(3, Channel, 'ChannelWriteString, " ...")
	else
	if Itm then
	<<  CheckLineFit(3, Channel, 'ChannelWriteString, " . ");
	    RecursiveChannelPrin1(Channel, Itm, Level) >> >>
    else
	CheckLineFit(3, Channel, 'ChannelWriteString, "...");
    CheckLineFit(1, Channel, 'ChannelWriteChar, char !) );
end;

syslsp procedure ChannelWriteVector(Channel, Vec, Level);
    if IntP LispVar PrinLevel and Level >= LispVar PrinLevel then
	ChannelWriteChar(Channel, char '!#)
    else
begin scalar Len, I;
    Level := Level + 1;
    CheckLineFit(1, Channel, 'ChannelWriteChar, char '![ );
    Len := VecLen VecInf Vec;
    If Len<0 then     
      return CheckLineFit(1, Channel, 'ChannelWriteChar, char '!] );
    I := 0;
LoopBegin:
    if not IntP LispVar PrinLength or I < LispVar PrinLength then
    <<  RecursiveChannelPrin2(Channel, VecItm(VecInf Vec, I), Level);
	if (I := I + 1) <= Len then
	<<  ChannelWriteBlankOrEOL Channel;
	    goto LoopBegin >> >>
    else
	CheckLineFit(3, Channel, 'ChannelWriteString, "...");	
    CheckLineFit(1, Channel, 'ChannelWriteChar, char '!] );
end;

syslsp procedure ChannelPrintVector(Channel, Vec, Level);
    if IntP LispVar PrinLevel and Level >= LispVar PrinLevel then
	ChannelWriteChar(Channel, char '!#)
    else
begin scalar Len, I;
    Level := Level + 1;
    CheckLineFit(1, Channel, 'ChannelWriteChar, char '![ );
    Len := VecLen VecInf Vec;
    If Len<0 then     
      return CheckLineFit(1, Channel, 'ChannelWriteChar, char '!] );
    I := 0;
LoopBegin:
    if not IntP LispVar PrinLength or I < LispVar PrinLength then
    <<  RecursiveChannelPrin1(Channel, VecItm(VecInf Vec, I), Level);
	if (I := I + 1) <= Len then
	<<  ChannelWriteBlankOrEOL Channel;
	    goto LoopBegin >> >>
    else
	CheckLineFit(3, Channel, 'ChannelWriteString, "...");	
    CheckLineFit(1, Channel, 'ChannelWriteChar, char '!] );
end;

syslsp procedure ChannelWriteEVector(Channel, EVec, Level);
begin
    scalar handler;
    if IntP LispVar PrinLevel and Level >= LispVar PrinLevel then
	ChannelWriteChar(Channel, char '!#)
    else
        if getd('object!-get!-handler!-quietly)
	   and (handler :=
	         object!-get!-handler!-quietly(EVec, 'ChannelPrin)) then
	   apply(handler, list(EVec, Channel, Level, NIL))
	else
	<< ChannelWriteString(Channel, "#<EVector ");
	   ChannelWriteSysInteger(Channel, EVecInf EVec,
					CompressedBinaryRadix);
	   ChannelWriteChar(Channel, char '!>); >>;
end;

syslsp procedure ChannelPrintEVector(Channel, EVec, Level);
begin
    scalar handler;
    if IntP LispVar PrinLevel and Level >= LispVar PrinLevel then
	ChannelWriteChar(Channel, char '!#)
    else
        if getd('object!-get!-handler!-quietly)
	   and (handler :=
	         object!-get!-handler!-quietly(EVec, 'ChannelPrin)) then
	   apply(handler, list(EVec, Channel, Level, T))
	else
	<< ChannelWriteString(Channel, "#<EVector ");
	   ChannelWriteSysInteger(Channel, EVecInf EVec,
					CompressedBinaryRadix);
	   ChannelWriteChar(Channel, char '!>); >>;
end;

syslsp procedure ChannelWriteWords(Channel, Itm);
begin scalar Len, I;
    ChannelWriteString(Channel, "#<Words:");
    Len := WrdLen WrdInf Itm;
    if Len < 0 then     
      return CheckLineFit(1, Channel, 'ChannelWriteChar, char '!> );
    I := 0;
LoopBegin:
    if not IntP LispVar PrinLength or I < LispVar PrinLength then
    <<  CheckLineFit(10, Channel, 'ChannelWriteInteger, WrdItm(WrdInf Itm, I));
	if (I := I + 1) <= Len then
	<<  ChannelWriteBlankOrEOL Channel;
	    goto LoopBegin >> >>
    else
	CheckLineFit(3, Channel, 'ChannelWriteString, "...");
    CheckLineFit(1, Channel, 'ChannelWriteChar, char '!> );
end;

syslsp procedure ChannelWriteHalfWords(Channel, Itm);
begin scalar Len, I;
    ChannelWriteString(Channel, "#<Halfwords:");
    Len := HalfWordLen HalfWordInf Itm;
    if Len < 0 then     
      return CheckLineFit(1, Channel, 'ChannelWriteChar, char '!> );
    I := 0;
LoopBegin:
    if not IntP LispVar PrinLength or I < LispVar PrinLength then
    <<  CheckLineFit(10, Channel, 'ChannelWriteInteger,
			HalfWordItm(HalfWordInf Itm, I));
	if (I := I + 1) <= Len then
	<<  ChannelWriteBlankOrEOL Channel;
	    goto LoopBegin >> >>
    else
	CheckLineFit(3, Channel, 'ChannelWriteString, "...");
    CheckLineFit(1, Channel, 'ChannelWriteChar, char '!> );
end;

syslsp procedure ChannelWriteBytes(Channel, Itm);
begin scalar Len, I;
    ChannelWriteString(Channel, "#<Bytes:");
    Len := StrLen StrInf Itm;
    if Len < 0 then     
      return CheckLineFit(1, Channel, 'ChannelWriteChar, char '!> );
    I := 0;
LoopBegin:
    if not IntP LispVar PrinLength or I < LispVar PrinLength then
    <<  CheckLineFit(10, Channel, 'ChannelWriteInteger, StrByt(StrInf Itm, I));
	if (I := I + 1) <= Len then
	<<  ChannelWriteBlankOrEOL Channel;
	    goto LoopBegin >> >>
    else
	CheckLineFit(3, Channel, 'ChannelWriteString, "...");
    CheckLineFit(1, Channel, 'ChannelWriteChar, char '!> );
end;

syslsp procedure ChannelPrin2(Channel, Itm);	%. Display Itm on Channel
    RecursiveChannelPrin2(Channel, Itm, 0);

syslsp procedure RecursiveChannelPrin2(Channel, Itm, Level);
<<  case Tag Itm of
	PosInt, NegInt:
	    CheckLineFit(10, Channel, 'ChannelWriteInteger, Itm);
	ID:
	    CheckLineFit(StrLen StrInf SymNam IDInf Itm + 1,
				Channel, 'ChannelWriteID, Itm);
	UNBOUND:
	    CheckLineFit(StrLen StrInf SymNam IDInf Itm + 12,
				Channel, 'ChannelWriteUnbound, Itm);
	STR:
	    CheckLineFit(StrLen StrInf Itm + 1,
				Channel, 'ChannelWriteString, Itm);
	CODE:
	    CheckLineFit(14, Channel, 'ChannelWriteCodePointer, Itm);
	FIXN:
	    CheckLineFit(20, Channel, 'ChannelWriteFixnum, Itm);
	FLTN:
	    CheckLineFit(30, Channel, 'ChannelWriteFloat, Itm);
	WRDS:
	    ChannelWriteWords(Channel, Itm);
	Halfwords:
	    ChannelWriteHalfWords(Channel, Itm);
	Bytes:
	    ChannelWriteBytes(Channel, Itm);
	PAIR:
	    ChannelWritePair(Channel, Itm, Level);
	VECT:
	    ChannelWriteVector(Channel, Itm, Level);
	EVECT:
	    ChannelWriteEVector(Channel, Itm, Level);
	default: 
	    CheckLineFit(20, Channel, 'ChannelWriteUnknownItem, Itm)
    end;
    Itm >>;

syslsp procedure Prin2 Itm;		%. ChannelPrin2 to current channel
    ChannelPrin2(LispVar OUT!*, Itm);

syslsp procedure ChannelPrin1(Channel, Itm);	%. Display Itm in READable form
    RecursiveChannelPrin1(Channel, Itm, 0);

syslsp procedure RecursiveChannelPrin1(Channel, Itm, Level);
<<  case Tag Itm of
	PosInt, NegInt:
	    CheckLineFit(10, Channel, 'ChannelWriteInteger, Itm);
	ID:				% leave room for possible escape chars
	    CheckLineFit(StrLen StrInf SymNam IDInf Itm + 5,
				Channel, 'ChannelPrintID, Itm);
	UNBOUND:			% leave room for possible escape chars
	    CheckLineFit(StrLen StrInf SymNam IDInf Itm + 16,
				Channel, 'ChannelPrintUnbound, Itm);
	STR:
	    CheckLineFit(StrLen StrInf Itm + 4,
				Channel, 'ChannelPrintString, Itm);
	CODE:
	    CheckLineFit(14, Channel, 'ChannelWriteCodePointer, Itm);
	FIXN:
	    CheckLineFit(20, Channel, 'ChannelWriteFixnum, Itm);
	FLTN:
	    CheckLineFit(20, Channel, 'ChannelWriteFloat, Itm);
	WRDS:
	    ChannelWriteWords(Channel, Itm);
	Halfwords:
	    ChannelWriteHalfWords(Channel, Itm);
	Bytes:
	    ChannelWriteBytes(Channel, Itm);
	PAIR:
	    ChannelPrintPair(Channel, Itm, Level);
	VECT:
	    ChannelPrintVector(Channel, Itm, Level);
	EVECT:
	    ChannelPrintEVector(Channel, Itm, Level);
	default: 
	    CheckLineFit(20, Channel, 'ChannelWriteUnknownItem, Itm)
    end;
    Itm >>;

syslsp procedure Prin1 Itm;		%. ChannelPrin1 to current output
    ChannelPrin1(LispVar OUT!*, Itm);

off SysLisp;

END;
