-- September 6, 1982 12:26 am
-- ParseWindowImpl.mesa
-- Last Edited by: Gnelson, November 3, 1983 3:17 pm

DIRECTORY Atom, ParseWindow, ParseTable, ViewerClasses, Rope, TiogaOps, Parser, Lexer, IO, Unparser, ParserGen;

ParseWindowImpl: MONITOR 
  IMPORTS Rope, TiogaOps, Parser, Lexer, Unparser, IO, ParserGen
  EXPORTS ParseWindow
= BEGIN OPEN ParseWindow;

ROPE: TYPE = Rope.ROPE;

NewHandle: PUBLIC PROC [v: ViewerClasses.Viewer]
 					 RETURNS [h: Handle] = 
  { h ← NEW[HandleRep]
  ;  h.ph ← Parser.NewHandle[]
  ;  h.ph.in ← Lexer.NewHandle[]
  ;  h.viewer ← v
  ;  h.content ← NIL
  ;  h.contentValid ← FALSE};

GenerateParser: PUBLIC PROC [h: Handle] =
{l: Lexer.Handle;
 ph: ParseTable.Handle;
 o: ParserGen.OpList;
 p: ParserGen.ProdList;
 c: ParserGen.Culprit;
 [l, ph, o, p, c] ← ParserGen.GenParser[h.grammerfilename];
 h.productions ← p;
 h.ph.table ← ph;
 h.ph.in ← l};

TiogaNode: TYPE = TiogaOps.Ref;

NodeProc: TYPE = PROC [n: TiogaNode, pw: Handle] 
                     RETURNS [keepgoing: BOOL];

NodeMap: PROC [ p: NodeProc, pw: Handle] RETURNS [didAllNodes: BOOL] =
  {  --! should lock the selection & the document
     root: TiogaNode ← TiogaOps.ViewerDoc[pw.viewer];
     RETURN[NM2[p, TiogaOps.FirstChild[root], pw]]} ;

NM2: PROC [p: NodeProc, n: TiogaNode, pw: Handle] 
       RETURNS [didAllNodes: BOOL] 
= {RETURN [n = NIL OR 
               (p[n, pw] AND NM2[p, TiogaOps.FirstChild[n], pw] 
                          AND NM2[p, TiogaOps.Next[n], pw])]};
                          
ParseViewer: PUBLIC PROC [pw: Handle] =
 {newContent: LIST OF NodeContent ← NIL;
  success: BOOL;
  tree: REF;
  text1: ROPE;
  text2: ROPE;
  n: TiogaNode ← TiogaOps.FirstChild[TiogaOps.ViewerDoc[pw.viewer]];
  pw.contentValid ← TRUE;
  WHILE n # NIL DO
    [success, tree, text1, text2] ←  PN2[n, pw];
    IF success THEN newContent ← CONS[[text1, text2, tree], newContent];
    n ← TiogaOps.Next[n];
  ENDLOOP;
  pw.content ← newContent};
  
PN2: PROC [n: TiogaNode, pw: Handle] 
 RETURNS [success: BOOL, tree: REF ANY, text1: ROPE, text2: ROPE]
 =
  {r: ROPE;
   vc: ParserGen.VerdictAndCulprit;
   errorMessage: ROPE ← NIL;
   text1 ← TiogaOps.GetRope[n];
   text2 ← TiogaOps.GetRope[TiogaOps.FirstChild[n]];
   -- next three lines try to skip parsing if old parsed result is present in pw.contents:
   { l: LIST OF NodeContent ← pw.content
   ; WHILE l # NIL AND (l.first.text1 # text1 OR l.first.text2 # text2) DO l ← l.rest ENDLOOP
   ; IF l # NIL 
    THEN {success ← TRUE; tree ← l.first.tree; text1 ← l.first.text1; text2 ← l.first.text2; RETURN}};
   IF ~ pw.contentValid THEN RETURN;
   pw.ph.in.in ← IO.RIS[Rope.Cat[text1, text2, " "]];
   pw.ph.in.eof ← FALSE;
   pw.ph.in.error ← NIL;
   pw.ph.in.Lex[];
   IF pw.ph.in.eof THEN {success ← TRUE; tree ← text1 ← text2 ← NIL; RETURN}; 
   pw.ph.Parse[];
   pw.ph.result ← tree ← CONS[pw.ph.result, NIL]; 
     -- necessary because IsGeneratedBy and Unparse work on the CAR of their argument
     -- and ignore the cdr.
   IF pw.ph.error = NIL AND pw.ph.eof 
     THEN {vc ← ParserGen.IsGeneratedBy[pw.productions, $S, pw.ph.result];
              IF vc.verdict # Yes
              THEN errorMessage ← "Not a WFF"
              ELSE {vc.culprit ← NIL; errorMessage ← NIL}}
     ELSE {vc.culprit ← NIL; 
             errorMessage ← IF pw.ph.error # NIL THEN pw.ph.error ELSE "Bad Syntax"};
   
   r ← Unparser.Unparse[pw.ph.result, vc.culprit, 60, pw.ph.table, pw.ph.openCount]; 
   tree ← NARROW[pw.ph.result, LIST OF REF ANY].first;
   IF pw.ph.error # NIL OR ~pw.ph.eof 
   THEN {r ← Rope.Cat[r, " \000", Rope.FromRefText[pw.ph.in.buf], "\000"];
            WHILE ~ IO.EndOf[pw.ph.in.in]
              DO r ← Rope.Cat[r, Rope.FromChar[IO.GetChar[pw.ph.in.in]]]
              ENDLOOP};
   {i: INT ← r.SkipTo[0, "\000"];
    j: INT;
    endOfHeader: INT = r.SkipTo[0, "\n"];
    firstLine: Rope.ROPE = r.Substr[0, endOfHeader]; 
    restOfLines: Rope.ROPE = 
      IF endOfHeader = r.Length THEN NIL ELSE r.Substr[endOfHeader + 1];
    Foo: SAFE PROC[root: TiogaOps.Ref] = TRUSTED
      {m: TiogaNode = IF TiogaOps.FirstChild[n] = NIL THEN n ELSE TiogaOps.FirstChild[n];
       TiogaOps.SelectNodes[viewer: pw.viewer, start:n, end:m, pendingDelete: TRUE, level:char];
       IF i = r.Length[] 
       THEN {TiogaOps.InsertRope[firstLine]; 
                TiogaOps.Break[]; 
                TiogaOps.Nest[]; 
                TiogaOps.InsertRope[restOfLines]}
       ELSE {j ← r.SkipTo[i + 1, "\000"];
               TiogaOps.InsertRope[Rope.Cat[r.Substr[0, i], r.Substr[i+1, j - i - 1], r.Substr[j+1]]];
               TiogaOps.SetSelection
                [pw.viewer, [n,  i], [n, j - 1]]}};
    TiogaOps.CallWithLocks[Foo, TiogaOps.ViewerDoc[pw.viewer]];
  success ← (errorMessage = NIL);
  text1 ← firstLine;
  text2 ← restOfLines}};

AddTree: PUBLIC PROC[pw: Handle, tree: REF]
= {r: ROPE ← Unparser.Unparse[LIST[tree], NIL, 60, pw.ph.table, 0];
    endOfHeader: INT = r.SkipTo[0, "\n"];
    firstLine: Rope.ROPE = r.Substr[0, endOfHeader];
    restOfLines: Rope.ROPE = 
      IF endOfHeader = r.Length THEN NIL ELSE r.Substr[endOfHeader + 1];
    nd:  TiogaNode ← TiogaOps.LastWithin[TiogaOps.ViewerDoc[pw.viewer]];
    Foo: SAFE PROC[root: TiogaOps.Ref] =  CHECKED
      {TiogaOps.SelectNodes[pw.viewer, nd, nd, node, FALSE];
       TiogaOps.Break[];
       TiogaOps.UnNest[];
       TiogaOps.InsertRope[firstLine];
       TiogaOps.Break[];
       TiogaOps.Nest[];
       TiogaOps.InsertRope[restOfLines]};
    TiogaOps.CallWithLocks[Foo, TiogaOps.ViewerDoc[pw.viewer]];
    pw.content ← CONS[[firstLine, restOfLines, tree], pw.content]};

AddText: PUBLIC PROC[pw: Handle, text1, text2: ROPE]  =
{nd:  TiogaNode ← TiogaOps.LastWithin[TiogaOps.ViewerDoc[pw.viewer]];
 Foo: SAFE PROC[root: TiogaOps.Ref] =  CHECKED
      {TiogaOps.SelectNodes[pw.viewer, nd, nd, node, FALSE];
       TiogaOps.Break[];
       TiogaOps.UnNest[];
       TiogaOps.InsertRope[text1];
       TiogaOps.Break[];
       TiogaOps.Nest[];
       TiogaOps.InsertRope[text2]};
    TiogaOps.CallWithLocks[Foo, TiogaOps.ViewerDoc[pw.viewer]]}; 

END.