parserlib/src/SeekRd.m3


MODULE SeekRd;
IMPORT TextList;
IMPORT Pathname;
IMPORT Stdio;
IMPORT Process;
IMPORT OSError;
IMPORT Rd, Wr;
IMPORT Fmt;
IMPORT Thread;
IMPORT FS;
IMPORT FileRd;
IMPORT File;
IMPORT SeekRdClass;
<* FATAL Thread.Alerted, Wr.Failure, Rd.Failure, OSError.E *>

TYPE
  NamedRd = FileRd.T BRANDED OBJECT
    p: Pathname.T;
  END;

PROCEDURE Error(message: TEXT) =
  BEGIN
    Wr.PutText(Stdio.stderr, message & "\n");
    Process.Exit(1);
  END Error;

PROCEDURE Open(p: Pathname.T; searchDirs: TextList.T := NIL): T =
  VAR
    cur := searchDirs;
    full: TEXT;
  BEGIN
    IF cur = NIL THEN
      TRY
        RETURN NEW(NamedRd, p := p).init(FS.OpenFileReadonly(p));
      EXCEPT
      | OSError.E => Error("Cannot open " & p); RETURN NIL;
      END;
    ELSE
      REPEAT
        full := cur.head & p;
        TRY
          RETURN NEW(NamedRd, p := full).init(FS.OpenFileReadonly(full));
        EXCEPT
        | OSError.E =>
        END;
        cur := cur.tail;
      UNTIL cur = NIL;
      Error("Cannot find " & p); RETURN NIL;
    END;
  END Open;

PROCEDURE LineNo(rd: Rd.T): INTEGER =
  VAR
    pos := Rd.Index(rd);
    result: INTEGER := 0;
  BEGIN
    TRY
      Rd.Seek(rd, 0);
      WHILE Rd.Index(rd) <= pos DO
        EVAL Rd.GetLine(rd);
        INC(result);
      END;
      Rd.Seek(rd, pos);
    EXCEPT
    | Rd.EndOfFile =>
    END;
    RETURN result;
  END LineNo;

PROCEDURE E(rd: T; message: TEXT) =
  VAR
    acc := "";
    lineNo: INTEGER;
  BEGIN
    IF rd # NIL THEN
      TYPECASE rd OF
      | NamedRd(nrd) =>
        acc := acc & nrd.p & " ";
        lineNo := LineNo(rd);
      | SeekRdClass.T(srd) =>
        lineNo := srd.lineNo();
      ELSE
        IF Rd.Seekable(rd) THEN
          lineNo := LineNo(rd);
        ELSE
          lineNo := -1;
        END;
      END;
      IF lineNo # -1 THEN
        acc := acc & "line " & Fmt.Int(lineNo) & ": ";
      END;
    END;
    acc := acc & message;
    Error(acc);
  END E;

PROCEDURE Stdin(): T =
  VAR
    hIn, hOut, hErr: File.T;
  BEGIN
    Process.GetStandardFileHandles(stdin:=hIn, stdout:=hOut, stderr:=hErr);
    RETURN NEW(SeekRdClass.T).init(hIn);
  END Stdin;

PROCEDURE DiscardPrevious(rd: T) =
  BEGIN
    TYPECASE rd OF SeekRdClass.T(r) =>
      r.discardPrevious();
    ELSE
    END;
  END DiscardPrevious;

BEGIN
END SeekRd.