-- PPHelperImpl.mesa
-- Russ Atkinson, June 9, 1982 3:49 pm

DIRECTORY
  Ascii USING [ControlZ, CR, FF, TAB],
  PPHelper, 
  PPLeaves USING [HTIndex, LTIndex], 
  PPP1 USING [IdOfLock], 
  PPTree USING [Handle, Link, NodeName], 
  PPTreeOps USING [NSons, NthSon],
  PPUtil USING [ShowChar], 
  Rope USING [Equal, ROPE, Text];

PPHelperImpl: CEDAR PROGRAM
  IMPORTS PPP1, PPTreeOps, PPUtil, Rope
  EXPORTS PPHelper
  SHARES Rope
  = BEGIN OPEN Ascii, PPLeaves, PPTreeOps, Tree: PPTree, Rope;

  LockId: HTIndex ← PPP1.IdOfLock[];
  
  -- this handle is used to hold on to the log stream
  indent, position, line, page: PUBLIC INT ← 0; 
  sizing: PUBLIC BOOL ← FALSE;
  lastChar: PUBLIC CHAR ← 0C; 

  lateTrigger: PUBLIC INT ← 80; 
  earlyTrigger: PUBLIC INT ← 64; 

  Excess: PUBLIC ERROR = CODE; 

  Init: PUBLIC PROC = {
    indent ← position ← line ← page ← 0;
    sizing ← FALSE;
    };
    
  GetInfo: PUBLIC PROC [t: Tree.Link] RETURNS [info: INT] = {
    DO WITH t SELECT FROM 
         hti: HTIndex =>
	  info ← IF hti = LockId THEN 0 ELSE hti.index;
         lti: LTIndex =>
	  info ← lti.index;
         x: Tree.Handle =>
	  {info ← x.info; 
                           IF info = 0 THEN 
                              FOR i: NAT IN [1..NSons[t]] WHILE info = 0 DO
                                  info ← GetInfo[NthSon[t, i]]
                                  ENDLOOP}
         ENDCASE => info ← 0; 
       EXIT
       ENDLOOP}; 
  
  GetAttrs: PUBLIC PROC [t: Tree.Link] RETURNS [a1,a2,a3: BOOL] = {
    WITH t SELECT FROM 
      x: Tree.Handle => RETURN [x.attr[1], x.attr[2], x.attr[3]]
      ENDCASE => a1 ← a2 ← a3 ← FALSE}; 
  
  WriteChar: PUBLIC PROC [c: CHAR] = {
    IF (lastChar ← c) < 40C
       THEN 
         {SELECT c FROM 
           TAB => {position ← position + 8; 
                   position ← position - position MOD 8; 
                   lastChar ← ' };
           CR => {position ← 0; line ← line + 1};
           FF => {position ← 0; line ← 0; page ← page + 1};
           ENDCASE}
       ELSE position ← position + 1; 
    IF sizing THEN 
       {IF lastChar # '  AND position >= lateTrigger THEN ERROR Excess; 
        RETURN}; 
    TRUSTED {PPUtil.ShowChar[c]}}; 
  
  WriteId: PUBLIC PROC [r: ROPE] = {
    s: Text ← NARROW[r];
    c: CHAR ← lastChar ← 'A;
    len: NAT ← IF s = NIL THEN 0 ELSE s.length;
    IF len = 0 THEN RETURN;
    c ← s[0];
    IF c IN ['B..'C] THEN
       {-- could be a trivial translation
        SELECT len FROM
          7 => IF Rope.Equal[s, "BOOLEAN"] THEN {s ← "BOOL"; len ← 4};
          9 => IF Rope.Equal[s, "CHARACTER"] THEN {s ← "CHAR"; len ← 4};
          ENDCASE};
    position ← position + len; 
    IF sizing THEN 
       {IF position > lateTrigger THEN ERROR Excess; 
        RETURN}; 
    FOR i: NAT IN [0..len) DO
        TRUSTED {PPUtil.ShowChar[s[i]]};
        ENDLOOP}; 
  
  WriteText: PUBLIC PROC [r: ROPE] = {
    -- we assume that the rope has been flattened
    --    an error occurs if it is not
    IF r # NIL THEN {
       s: Text = NARROW[r];
       FOR i: NAT IN [0..s.length) DO
           c: CHAR ← s[i];
	   IF c = Ascii.ControlZ THEN
	      {WriteChar[Ascii.CR]; RETURN};
           WriteChar[c]
           ENDLOOP}}; 
  
  WriteQuotedText: PUBLIC PROC [r: ROPE] = {
    -- we assume that the rope has been flattened
    --    an error occurs if it is not
    -- we also assume that the scanner has escape marks in
    --    but that the leading and trailing quotes are gone
    WriteChar['"]; 
    IF r # NIL THEN {
       s: Text = NARROW[r];
       FOR i: NAT IN [0..s.length) DO
           WriteChar[s[i]]; 
           ENDLOOP}; 
    WriteChar['"]}; 
  
  Indent: PUBLIC PROC = {
    count: INT ← indent - position;
    WHILE (count←count-1) >= 0 DO
          WriteChar[' ]
          ENDLOOP}; 
  
  Break: PUBLIC PROC = {
    IF position > indent THEN WriteChar[CR]; 
    Indent[]}; 
 

  END.