-- file SMReaderImpl.mesa
-- last modified by Satterthwaite, August 4, 1983 10:01 am
-- last edit by Schmidt,  May 16, 1983 5:27 pm

DIRECTORY
  CS: TYPE USING [RopeFromStamp, SetPFCodes, z],
  IO: TYPE USING [
    atom, card, EndOf, GetChar, GetIndex, Handle, int, Put, PutChar, PutF,
    rope, SetIndex, STREAM, string],
  SMP1: TYPE --P1-- USING [InstallParseTable, Parse],
  Runtime: TYPE USING [GetTableBase],
  Rope: TYPE USING [Map, ROPE, Text],
  SMCommentTable: TYPE USING [Index, Ref, Text],
  SMCommentTableOps: TYPE USING [Create, Explode, FindNext, Reset],
  SMFI: TYPE USING [BcdFileInfo, SrcFileInfo],
  SMLDriver: TYPE USING [Create],
  SMOps: TYPE USING [ModelState, MS],
  SMParseData: TYPE USING [],
  SMUtil: TYPE USING [],
  SMTree: TYPE Tree USING [
    AttrId, Handle, Id, Link, NodeName, Name, Number, Stamp, Text,
    null, nullId, nullName],
  SMTreeOps: TYPE --TreeOps-- USING [
    TM, Scan, Create, GetName, NthSon, PopTree, ScanSons];

-- this monitor protects the model parsing code

SMReaderImpl: CEDAR MONITOR
    IMPORTS
      CS, IO, SMP1, Rope, Runtime, SMCommentTableOps, SMLDriver, SMTreeOps,
      SMParseData
    EXPORTS SMUtil, SMOps ~ { 
  OPEN P1~~SMP1, Tree~~SMTree, TreeOps~~SMTreeOps;

  tabSize: CARDINAL ~ 8;		-- usually 8
    
  -- mds usage
  parserCondition: CONDITION;
  parserBusy: BOOL ← FALSE;
  -- end of mds usage
	
  AcquireModelParser: ENTRY PROC ~ {
    ENABLE UNWIND => {NULL};
    WHILE parserBusy DO WAIT parserCondition ENDLOOP;
    parserBusy ← TRUE};
  	
  ReleaseModelParser: ENTRY PROC ~ {
    ENABLE UNWIND => {NULL};
    parserBusy ← FALSE;
    NOTIFY parserCondition};
  	

-- parsing sequencing

  ParseStream: PUBLIC PROC[m: SMOps.MS, source: IO.STREAM] RETURNS[root: Tree.Link] ~ {
    ENABLE UNWIND => {ReleaseModelParser[]};
    AcquireModelParser[];
    IF m.comments # NIL THEN (m.comments).Reset;
     {complete: BOOL ← TRUE;
      nTokens, nErrors: CARDINAL ← 0;
      TRUSTED {[complete, nTokens, nErrors] ← P1.Parse[m, source]};
      root ← (IF complete --AND nErrors = 0-- THEN (m.tm).PopTree ELSE Tree.null);
      };
    ReleaseModelParser[]};

  SourceIndex: TYPE ~ SMCommentTable.Index;
  
 -- basic io

  WriteQuotedText: PROC[s: Rope.ROPE, out: IO.Handle] ~ {

    EachChar: PROC[c: CHAR] RETURNS[BOOL←FALSE] ~ {
      out.PutChar[c];
      IF c = '" THEN out.PutChar['"]};

    IF s # NIL THEN {
      out.PutChar['"]; [] ← Rope.Map[base~s, action~EachChar]; out.PutChar['"]};
    };

  Indent: PROC[n: NAT, out: IO.Handle] ~ {
    out.PutChar['\n];
    THROUGH [1..n/tabSize] DO out.PutChar['\t] ENDLOOP;
    THROUGH [1.. n MOD tabSize] DO out.PutChar[' ] ENDLOOP};

 -- tree printing

  PrintTree: PUBLIC PROC[m: SMOps.MS, t: Tree.Link] ~ {
    PrintSubTree[m.out, t, 0];  
    (m.out).PutF["\n"];
    IF m.comments # NIL THEN {
      index: SourceIndex ← 0;
      comments: BOOL ← FALSE;
      DO
	ref: SMCommentTable.Ref ~ (m.comments).FindNext[index];
        IF ref = NIL THEN EXIT;
	IF ~comments THEN (m.out).Put[IO.string["\nComments:"L]];  comments ← TRUE;
	(m.out).Put[IO.string["\n  "L]];  (m.out).Put[IO.card[index]];
	index ← SMCommentTableOps.Explode[ref].start+1;
        ENDLOOP;
      IF comments THEN (m.out).PutF["\n"]};
    };

  PrintSubTree: PUBLIC PROC[out: IO.Handle, t: Tree.Link, nBlanks: NAT] ~ {
    OPEN Tree;

    Printer: TreeOps.Scan ~ TRUSTED {
      Indent[nBlanks, out];
      IF t = Tree.null THEN out.Put[IO.string["<empty>"L]]
      ELSE
	WITH t SELECT FROM
	  name: Tree.Name => PrintName[name, out];
	  id: Tree.Id => PrintId[id, out];
	  node: Tree.Handle => {
	    WriteNodeName[node.name, out];
	    IF node.info # 0 THEN {
	      out.Put[IO.string["  info="L]]; out.Put[IO.card[node.info]]};
	    IF node.attrs # ALL[FALSE] THEN {
	      IF node.info = 0 THEN out.PutChar[' ];
	      out.PutChar['(];
	      FOR i: Tree.AttrId IN Tree.AttrId DO
		IF node.attrs[i] THEN out.PutChar[VAL[i+'0.ORD]] ENDLOOP;
	      out.PutChar[')]};
	    nBlanks ← nBlanks + 2;
	    TreeOps.ScanSons[t, Printer];
	    nBlanks ← nBlanks - 2};
	  fiSrc: SMFI.SrcFileInfo => out.PutF["(fiSrc: %s)", IO.rope[fiSrc.localName]];
	  fiBcd: SMFI.BcdFileInfo => out.PutF["(fiBcd: %s)", IO.rope[fiBcd.localName]];
	  ENDCASE => PrintLiteral[t, out];
	};

    Printer[t]};

  WriteNodeName: PROC[n: Tree.NodeName, out: IO.Handle] ~ {
    out.Put[IO.string[SELECT n FROM
       $none => "none"L,
       $lambda => "lambda"L,  
       $let => "let"L, 
       $arrow => "arrow"L,  
       $apply => "apply"L,
       $applyDefault => "apply*"L, 	 
       $subscript => "subscript"L,	 
       $union => "union"L,	 
       $then => "then"L,	 
       $exclusion => "exclusion"L,	 
       $restriction => "restriction"L,	 
       $splitUpper => "splitUpper"L,	 
       $splitLower => "splitLower"L,	 
       $group => "group"L,	 
       $decl => "decl"L,	 
       $declElem => "declElem"L, 
       $bind => "bind"L,	 
       $bindRec => "bindRec"L,	 
       $bindElem => "bindElem"L, 
       $type => "type"L, 
       $env => "env"L, 
       $nil => "nil"L, 
       $control => "control"L, 
       $unitId => "unitId"L,
       $uiList => "uiList"L,
       $unQuote => "unQuote"L,
       $typeTYPE => "typeTYPE"L,
       $typeDECL => "typeDECL"L,
       $typeBINDING => "typeBINDING"L,
       $typePATTERN => "typePATTERN"L,
       $typeSTRING => "typeSTRING"L,
       $nBind => "nBind"L,
       $nBindRec => "nBindRec"L,
       $stamp => "stamp"L,
       $cross => "cross"L,
       $cross2 => "cross2"L,
       $locator => "locator"L,
       ENDCASE => ERROR]]
     };
       	
  PrintLiteral: PROC[t: Tree.Link, out: IO.Handle] ~ {
    WITH t SELECT FROM
      text: Tree.Text => WriteQuotedText[text, out];
      num: Tree.Number => out.Put[IO.int[num↑]];
      stamp: Tree.Stamp => out.Put[IO.rope[CS.RopeFromStamp[stamp↑]]];
      n: REF LONG CARDINAL => out.Put[IO.card[n↑]];
      ENDCASE => out.PutChar['?]
    };

  PrintName: PROC[name: Tree.Name, out: IO.Handle] ~ {
    out.Put[IF name = Tree.nullName THEN IO.string["(anon)"L] ELSE IO.atom[name]]};

  PrintId: PROC[id: Tree.Id, out: IO.Handle] ~ {
    IF id = Tree.nullId THEN out.Put[IO.string["<null>"L]]
    ELSE {
      d: Tree.Handle ~ (IF id.db.name = $decl THEN id.db ELSE NARROW[id.db[1]]);
      out.Put[IO.atom[TreeOps.GetName[TreeOps.NthSon[d[id.p], 1]]]];
      out.PutChar['[]; out.Put[IO.card[id.p]]; out.PutChar[']]};
    };

  NewModel: PUBLIC PROC[in, out, msgout: IO.Handle] RETURNS[SMOps.MS] ~ {
    tm: TreeOps.TM;
    CS.SetPFCodes[out];	-- causes start trap, which initializes CS.z
    tm ← TreeOps.Create[CS.z];
    RETURN [(CS.z).NEW[SMOps.ModelState ← [
      in~in, out~out, msgOut~msgout,
      z~CS.z,
      tm~tm,
      comments~SMCommentTableOps.Create[CS.z],
      ls~SMLDriver.Create[CS.z, tm, out]]]]
    };
	
-- source is the input file
-- out is where to print the error message
-- message is the message
-- tokenIndex is the position of the error in the text

  ErrorContext: PUBLIC PROC[source, out: IO.Handle, message: Rope.ROPE, tokenIndex: INT] ~ {
    saveIndex: INT ~ source.GetIndex[];
    lineIndex, start: INT ← tokenIndex;
    char: CHAR;
    FOR n: NAT IN [1..100] UNTIL lineIndex = 0 DO
      lineIndex ← lineIndex - 1;
      source.SetIndex[lineIndex];
      IF source.GetChar[] = '\n THEN EXIT;
      start ← lineIndex;
      ENDLOOP;
    source.SetIndex[start];		-- start points to the first char on the line
    FOR n: NAT IN [1..100] UNTIL source.EndOf[] DO
      char ← source.GetChar[];
      SELECT char FROM
	'\n => EXIT;
	ENDCASE => out.PutChar[char];
      ENDLOOP;
    out.PutChar['\n];
    source.SetIndex[start];		-- start points to the first char on the line
    UNTIL source.GetIndex[] = tokenIndex OR source.EndOf[] DO
      char ← source.GetChar[];	-- print out right number of spaces
      out.PutChar[IF char = '\t THEN '\t ELSE ' ];
      ENDLOOP;
    out.PutF["↑ %s [%d]\n", IO.rope[message], IO.card[tokenIndex]];
    source.SetIndex[saveIndex]};
    
    
 -- initialization code

  TRUSTED {P1.InstallParseTable[Runtime.GetTableBase[LOOPHOLE[SMParseData]]]};

  }.