-- Last Modified by JHM
--  Schmidt, August 21, 1982 2:59 pm

DIRECTORY
  Disp: TYPE USING [],
  IO: TYPE USING [char, GetChar, Put, PutChar, rope, ROPE],
  PL: TYPE USING [cdebug, EndDisplay, Environment, Insert, IS, LSTNode, Node,
  	NodeType, OS, rASS, rCAT, rCATL, rCLOSURE, rCOMB, rDELETE, rEQUAL,
  	RErr, rFAIL, rFCN, rGOBBLE, rGTR, rHOLE, rID, rITER, rLST, rMAPPLY,
  	rMINUS, rNUM, rOPT, rPALT, rPAPPLY, rPATTERN, rPLUS, rPROG, rSEQ,
  	rSEQOF, rSEQOFC, rSTR, rTILDE, rWILD, SN, Symbol],
  PString: TYPE USING [EmptyS, Item, MakeNUM, NewStream, Stream],
  Rope: TYPE USING [Fetch, Length, ROPE],
  SafeStorage: TYPE USING [NarrowRefFault];

DispImpl: CEDAR PROGRAM IMPORTS P:PL, S: PString, Rope, IO, SafeStorage EXPORTS Disp  = {
OPEN Rope, IO, PL;
NumLines: INTEGER = 10;
Environment: TYPE = PL.Environment;
LSTNode: TYPE = PL.LSTNode;
NodeType: TYPE = PL.NodeType;
Node: TYPE = PL.Node;
Symbol: TYPE = PL.Symbol;
Stream: TYPE = PString.Stream;
cdebug: BOOLEAN = PL.cdebug;
WP: TYPE = PROC[CHARACTER];
--
WC: WP;
charCount: CARDINAL ← 0;
DispCnt: INTEGER ← 0;
AskEnd: BOOLEAN ← TRUE;
ConfirmChar: CHARACTER ← '\n;
LineLength: CARDINAL = 60;
MoreFlag: BOOLEAN;

Precedence: TYPE = [0..256);
Operator: ARRAY NodeType OF ROPE; 

CharsPerLine: CARDINAL = 72;
cIn: CARDINAL; -- current indentation of the line to be printed;
cPos: CARDINAL; -- current character position;
broken: [0..1]; -- 1 iff lines at current indentation should be put on multiple lines
Line: ARRAY [0..CharsPerLine) OF RECORD[break: BOOLEAN, indent: [0..128), c: CHARACTER];
-- Invariant:
      -- Line[0...cIn).c = SP
      -- For i in [cIn...cPos)Line[i].c contains characters
      -- If Line[i].break then we can start a new line there indented by Line[i].indent.  This indent value us always greater than cIn.
      -- cPos < CharsPerLine (i.e. we empty immediately when full)

-- this routine is necessary only until compiler is fixed (5/23/80)

ClearLine: PUBLIC PROC = {};

ClearScreen: PUBLIC PROC = {charCount ← DispCnt ← 0};

Confirm: PUBLIC PROC RETURNS [BOOLEAN] =
   {ConfirmChar ← P.IS.GetChar[]; RETURN[ ConfirmChar= '\n]};

DispReset: PUBLIC PROC = {
DispCnt ← 0;
AskEnd ← TRUE;
MoreFlag ← TRUE;
WC ← MyWriteProcedure;
};

DispSetup: PUBLIC PROC = {
DispReset[];
[] ← P.Insert["print",[,,ZARY[PrintRoutine]]];
};

MyWriteProcedure: PUBLIC PROC[c: CHARACTER] = {
P.OS.PutChar[c];
charCount ← IF c='\n THEN 0 ELSE charCount+1;
IF ~(charCount>CharsPerLine OR c = '\n) OR ~AskEnd THEN RETURN;
charCount ← 0;
DispCnt ← DispCnt + 1;
IF DispCnt >= NumLines AND ~MoreFlag THEN {
	   P.OS.Put[char['\n], rope[" ......\n"]];
	   P.EndDisplay;
      };
};

Print: PUBLIC PROC[n: Node] = {
{ ENABLE P.EndDisplay => CONTINUE;
i: CARDINAL;
      cIn ← cPos ← broken ← 0;
      FOR i IN [0..CharsPerLine) DO
	   Line[i].break ← FALSE;
	   ENDLOOP;
PrintExp[n,0,0,0];broken ← 1;NL[0];
};
};

NL: PROC[i: CARDINAL] =
      {
      IF i<cIn+broken THEN -- flush out line
	   { j: CARDINAL;
	   FOR j IN [0..cPos)
		 DO
		 WC[Line[j].c];
		 Line[j]←[FALSE,,' ];
		 ENDLOOP;
	   WC['\n];
	   IF i<cIn THEN broken ← 0;
	   cIn  ← cPos ← i;
	   }
      ELSE {
	   Line[cPos].break ← TRUE;
	   Line[cPos].indent ← i;
	   };
      };

PC: PROC[c: CHARACTER] = {
      Line[cPos].c ← c;
      cPos ← cPos + 1;
      IF cPos=CharsPerLine THEN -- Time to flush line
	   {
	   i, j, k, l: CARDINAL;
	   mindent: CARDINAL ← 77777B;
	   FOR i IN [cIn+1 ..CharsPerLine) DO
		 IF Line[i].break AND Line[i].indent<mindent THEN
		 mindent ← Line[i].indent;
		 ENDLOOP;
	   FOR i ← 0, i+1 UNTIL i=CharsPerLine OR 
                       i>cIn AND Line[i].break AND
                        Line[i].indent=mindent
		 DO
		 WC[Line[i].c]
		 ENDLOOP;
	   WC['\n];
	   IF mindent=77777B THEN cPos ← cIn
	   ELSE {
		 -- We're now at greater indentation than before
		 --Add blanks
		 FOR j IN [cIn..mindent) DO
		       Line[j] ← [FALSE,,' ]; ENDLOOP;
		 j ← cIn ← mindent;
		 -- shift characters to the left
		 k ← i;
		       DO
		       Line[j] ← Line[k];
		       j ← j+1;
		       k ← k+1;
		       IF k=CharsPerLine THEN EXIT;
		       IF Line[k].break AND Line[k].indent=cIn THEN
			     { -- flush again
			     FOR l IN [0..j)
				  DO
				  WC[Line[l].c]
				  ENDLOOP;
			     WC['\n];
			     j ← cIn;
			     };
		       ENDLOOP;
		 cPos ← j;
		 broken ← 1;
		 };
	   FOR i IN [cPos..CharsPerLine) DO
		 Line[i].break ← FALSE ENDLOOP;
	   };
      };

PrintExp: PROC[n: Node,in: CARDINAL,lp,rp: Precedence] = {
-- This routine never calls Alloc
DO -- to eliminate tail recursion
IF n = NIL THEN PS["NIL(error)"]
ELSE IF ISTYPE[n, rFAIL] OR ISTYPE[n, rWILD] OR ISTYPE[n, rHOLE] THEN PS[Operator[n.Type]]
ELSE
{W: PROC[left, right: Node] RETURNS [loop: BOOLEAN]=
     {lprec, rprec: Precedence;
      SELECT n.Type FROM
       PROG => {lprec ←2; rprec ← 2};
       ASS => {lprec ← 16; rprec ← 4};
       PALT => {lprec ← 6; rprec ← 6};
       GTR => {lprec ← 8; rprec ← 8};
       ENDCASE => {lprec ← 10; rprec ← 11};
      IF lp>lprec OR rprec<rp THEN
	   {
	   PC['(];
	   PrintExp[left,in+1,0,lprec];
	   NL[in+1];
	   PS[Operator[n.Type]];
	   PrintExp[right,in+1,rprec, 0];
	   PC[')];
	   RETURN[FALSE]}
      ELSE {
	   PrintExp[left,in,lp,lprec];
	   NL[in];
	   PS[Operator[n.Type]];
	   n ← right;
	   lp ← rprec;
	   RETURN[TRUE];
	   };
      };
WITH n SELECT FROM
m: rNUM => PrintExp[P.SN[S.MakeNUM[m.num]],in,lp,rp];
m: rSTR =>
      {
      -- No Allocs done in here, by called procedures either!
      ns, ns1: Stream;
      ns ← S.NewStream[m.str];
      ns1 ← S.NewStream[m.str];
      WHILE ~S.EmptyS[ns1] DO
	   c: CHARACTER;
	   [c, ns1] ← S.Item[ns1];
	   IF c NOT IN ['0..'9] THEN GOTO NonNum;
	   REPEAT
	   NonNum =>
		 {
		 PC['"];
		 WHILE ~S.EmptyS[ns] DO
		       {
		       c: CHARACTER;
			[c, ns] ← S.Item[ns];
		       SELECT c FROM
		       '", '↑ => { PC['↑]; PC[c] };
		       36C => PS["↑036"];
		       '\177 => PS["↑177"];
		       IN [' ..'\177) => PC[c];
		       ENDCASE => {
			     PC['↑]; PC[c+100B];
			     };
		       };
		       ENDLOOP;
		 PC['"];
		 };
	   FINISHED =>
		 IF S.EmptyS[ns] THEN
		       { PC['"]; PC['"] }
		 ELSE WHILE ~S.EmptyS[ns] DO
		       c: CHARACTER;
			[c,ns] ← S.Item[ns];
			PC[c];
		       ENDLOOP;
	   ENDLOOP;
      };
m: rCLOSURE => 
      {
      e: Environment;
      IF m.env=NIL THEN { n←m.exp; LOOP };
      PC['(];
      in ← in+1;
      PrintExp[m.exp,in,0,0];
      NL[in];
      PS[" where "];
      e ← m.env;
      UNTIL e=NIL DO
	   PrintString1[e.name.name];
	   PC['=];
	   PrintExp[e.val,in,0,0]; NL[in];
	   e ← e.next;
	   IF e#NIL THEN PS[" and "]
	   ENDLOOP;
      PC[')];
      };
m: rID => PrintString1[m.name.name];
m: rPATTERN => {
      PC['{]; PrintExp[m.pattern,in+1,0,0]; PC['}]
      };
m: rLST => 
      IF m.listhead=NIL THEN PS["[]"]
      ELSE 
	   {l: rLST ← m;
	   PC['[];
	   DO
		 PrintExp[l.listhead,in+1,0,0];
		 l ← l.listtail;
		 IF l.Type ~= LST THEN
		       {PS["TRASHED LST TAIL!!!"];
		       EXIT};
		 IF l.listhead = NIL THEN EXIT;
		 PC[',]; NL[in+1];
		 ENDLOOP;
	   PC[']];
	   };
m: rCOMB =>
      IF rp>12 THEN
	   {
	   PC['(];
	   PrintString1[m.proc.name];
	   PC[' ];
	   NL[in+1];
	   PrintExp[m.parm,in+1,12, 0];
	   PC[')];
	   }
      ELSE
	   {
	   PrintString1[m.proc.name];
	   PC[' ];
	   NL[in];
	   n←m.parm;
	   lp ← 12;
	   LOOP;
	   };
m: rTILDE =>
      IF rp>12 THEN
	   {
	   PS["(~"];NL[in+1];
	   PrintExp[m.not,in+1,12, 0];
	   PC[')];
	   }
      ELSE
	   {
	   PC['~];NL[in];
	   n←m.not;
	   lp ← 12;
	   LOOP;
	   };
m: rFCN =>
      {
      IF lp>16 OR 0<rp THEN
	   {
	   PC['(];
	   PrintExp[m.parms,in+1,0,16];
	   PS[": "];
	   NL[in+1];
	   PrintExp[m.fcn,in+1,0, 0];
	   PC[')];
	   }
      ELSE {
	   PrintExp[m.parms,in,lp,16];
	   PS[": "];
	   NL[in];
	   n ← m.fcn;
	   lp ← 0;
	   LOOP;
	   };
      };
m: rCAT => IF W[m.left, m.right] THEN LOOP;
m: rCATL => IF W[m.left, m.right] THEN LOOP;
m: rEQUAL => IF W[m.left, m.right] THEN LOOP;
m: rGTR => IF W[m.left, m.right] THEN LOOP;
m: rPALT => IF W[m.left, m.right] THEN LOOP;
m: rPAPPLY => IF W[m.left, m.right] THEN LOOP;
m: rMAPPLY => IF W[m.left, m.right] THEN LOOP;
m: rGOBBLE => IF W[m.left, m.right] THEN LOOP;
m: rITER => IF W[m.left, m.right] THEN LOOP;
m: rPROG => IF W[m.left, m.right] THEN LOOP;
m: rPLUS => IF W[m.left, m.right] THEN LOOP;
m: rMINUS => IF W[m.left, m.right] THEN LOOP;
m: rSEQ => IF W[m.left, m.right] THEN LOOP;
m: rASS =>
      {
      IF lp>16 OR 4<rp THEN
	   {
	   PC['(];
	   PrintString1[m.lhs.name];
	   NL[in+1];
	   PS["←"];
	   PrintExp[m.rhs,in+1,4, 0];
	   PC[')];
	   }
      ELSE {
	   PrintString1[m.lhs.name];
	   NL[in];
	   PS["←"];
	   n ← m.rhs;
	   lp ← 4;
	   LOOP;
	   };
      };
m: rOPT => {PrintExp[m.pat,in,lp, 14]; PS[Operator[n.Type]]};
m: rDELETE => {PrintExp[m.pat,in,lp, 14]; PS[Operator[n.Type]]};
m: rSEQOF => {PrintExp[m.pat,in,lp, 14]; PS[Operator[n.Type]]};
m: rSEQOFC => {PrintExp[m.pat,in,lp, 14]; PS[Operator[n.Type]]};
ENDCASE => {PS["TRASHED NODE!!! Type= "];PC[LOOPHOLE[n.Type]]};
RETURN;
}ENDLOOP;
};

PrintRoutine: PROC[n1: Node] RETURNS[Node] = {
-- zary
-- print on terminal the entire incoming string, as is
-- must be string coming in
PrintString[NARROW[n1, rSTR ! SafeStorage.NarrowRefFault =>
P.RErr["print requires a string as input"]].str] ;
RETURN[n1];
};

PrintString: PUBLIC PROC[s: ROPE] = {
      ns: Stream;
      ns ← S.NewStream[s];
      WHILE ~S.EmptyS[ns] DO c:CHARACTER; [c, ns] ← S.Item[ns]; WC[c]; ENDLOOP;
};

PrintString1: PROC[s: ROPE] = {
      ns: Stream;
      ns ← S.NewStream[s];
      WHILE ~S.EmptyS[ns] DO c:CHARACTER; [c, ns] ← S.Item[ns]; PC[c]; ENDLOOP;
};

PS: PROC[s: ROPE] = {
FOR i: LONG INTEGER IN [0..Length[s]) DO PC[Fetch[s,i]] ENDLOOP;
};

ToggleAllPrint: PUBLIC PROC RETURNS[BOOLEAN] = {
RETURN[TRUE];
};

ToggleMore: PUBLIC PROC RETURNS[BOOLEAN] = {
RETURN[MoreFlag ← ~MoreFlag];
};

t: NodeType;
FOR t IN NodeType DO Operator[t] ← "???" ENDLOOP;
Operator[HOLE] ← "...";
Operator[CAT] ← "  ";
Operator[FAIL] ← "fail";
Operator[WILD] ← "#";
Operator[CATL] ← ",,";
Operator[EQUAL] ← "=";
Operator[GTR] ← ">";
Operator[PALT] ← "| ";
Operator[PAPPLY] ← "/";
Operator[MAPPLY] ← "//";
Operator[GOBBLE] ← "///";
Operator[ITER] ← "%";
Operator[FCN] ← ":";
Operator[ASS] ← "←";
Operator[PROG] ← ";";
Operator[SEQOF] ← "!";
Operator[SEQOFC] ← ",!";
Operator[DELETE] ← "*";
Operator[PLUS] ← "+";
Operator[MINUS] ← "-";
Operator[TILDE] ← "~";
Operator[SEQ] ← "--";
Operator[OPT] ← "?";

}.