(* Copyright 1991 Digital Equipment Corporation.               *)
(* Distributed only by permission.                             *)

MODULE ObLibM3;
IMPORT SynLocation, Text, ObLib, ObValue, Obliq, Rd, Wr, Process, FloatMode,
  Thread, Stdio, Pipe, FileRd, FileWr, OSError, Fmt, Lex, TextRd, TextWr, 
  Convert, NetObj, Pickle, Word;

  VAR setupDone := FALSE;

  PROCEDURE PackageSetup() =
  BEGIN
    IF NOT setupDone THEN
      setupDone := TRUE;
      Setup();
    END;
  END PackageSetup;

  PROCEDURE Setup() =
  BEGIN
    SetupRd();
    SetupWr();
    SetupLex();
    SetupFmt();
    SetupWord();
    SetupPickle();
    SetupProc();
  END Setup;

(* ============ "rd" package ============ *)

TYPE

  RdCode = {Failure, EofFailure, New, Stdin, Open, GetChar, Eof, UnGetChar, 
            CharsReady, GetText, GetLine, Index, Length, Seek, Close, 
            Intermittent, Seekable, Closed};

  RdOpCode =  
    ObLib.OpCode OBJECT
        code: RdCode;
      END;
    
  PackageRd = 
    ObLib.T OBJECT
      OVERRIDES
        Eval:=EvalRd;
      END;

  PROCEDURE IsRd(self: ValRd; other: ObValue.ValAnything): BOOLEAN =
  BEGIN
    TYPECASE other OF ValRd(oth)=> RETURN self.rd = oth.rd;
    ELSE RETURN FALSE END;
  END IsRd;

  PROCEDURE CopyRd(self: ObValue.ValAnything; tbl: ObValue.Tbl;
    loc: SynLocation.T): ObValue.ValAnything RAISES {ObValue.Error} =
  BEGIN
    RETURN self;
  END CopyRd;

  VAR rdFailureException, rdEofFailureException: ObValue.ValException;

  PROCEDURE NewRdOC(name: TEXT; arity: INTEGER; code: RdCode)
    : RdOpCode =
  BEGIN
    RETURN NEW(RdOpCode, name:=name, arity:=arity, code:=code);
  END NewRdOC;

  VAR true, false: ObValue.ValBool;

  PROCEDURE SetupRd() =
  TYPE OpCodes = ARRAY OF ObLib.OpCode;
  VAR opCodes: REF OpCodes;
  BEGIN
    opCodes := NEW(REF OpCodes, NUMBER(RdCode));
    opCodes^ :=
      OpCodes{
      NewRdOC("failure", -1, RdCode.Failure), 
      NewRdOC("eofFailure", -1, RdCode.EofFailure), 
      NewRdOC("new", 1, RdCode.New), 
      NewRdOC("stdin", -1, RdCode.Stdin),
      NewRdOC("open", 2, RdCode.Open),
      NewRdOC("getChar", 1, RdCode.GetChar), 
      NewRdOC("eof", 1, RdCode.Eof), 
      NewRdOC("unGetChar", 1, RdCode.UnGetChar), 
      NewRdOC("charsReady", 1, RdCode.CharsReady), 
      NewRdOC("getText", 2, RdCode.GetText), 
      NewRdOC("getLine", 1, RdCode.GetLine), 
      NewRdOC("index", 1, RdCode.Index), 
      NewRdOC("length", 1, RdCode.Length), 
      NewRdOC("seek", 2, RdCode.Seek), 
      NewRdOC("close", 1, RdCode.Close), 
      NewRdOC("intermittent", 1, RdCode.Intermittent), 
      NewRdOC("seekable", 1, RdCode.Seekable), 
      NewRdOC("closed", 1, RdCode.Closed)
      };
    ObLib.Register(
      NEW(PackageRd, name:="rd", opCodes:=opCodes));
    rdFailureException := NEW(ObValue.ValException, name:="rd_failure");
    rdEofFailureException := NEW(ObValue.ValException, name:="rd_eofFailure");
    true := NEW(ObValue.ValBool, bool:=TRUE);
    false := NEW(ObValue.ValBool, bool:=FALSE);
  END SetupRd;

  PROCEDURE EvalRd(self: PackageRd; opCode: ObLib.OpCode; 
      arity: ObLib.OpArity; READONLY args: ObValue.ArgArray; 
      temp: BOOLEAN; loc: SynLocation.T)
      : ObValue.Val RAISES {ObValue.Error, ObValue.Exception} =
    VAR rd1: Rd.T; text1: TEXT; int1: INTEGER; fileSys1: ObValue.ValFileSystem;
    BEGIN
      TRY
        CASE NARROW(opCode, RdOpCode).code OF
        | RdCode.Failure => 
            RETURN rdFailureException;
        | RdCode.EofFailure => 
            RETURN rdEofFailureException;
        | RdCode.New => 
            TYPECASE args[1] OF | ObValue.ValText(node) => text1:=node.text;
            ELSE ObValue.BadArgType(1, "text", self.name, opCode.name, loc); END;
            rd1 := TextRd.New(text1);
            RETURN NEW(ValRd, what:="<a reader>", picklable:=FALSE, rd:=rd1);
       | RdCode.Stdin => 
            RETURN NEW(ValRd, what:="<stdin reader>", picklable:=FALSE, 
              rd:=Stdio.stdin);
       | RdCode.Open => 
            TYPECASE args[1] OF | ObValue.ValFileSystem(node) => fileSys1:=node;
            ELSE ObValue.BadArgType(1, "file system", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValText(node) => text1:=node.text;
            ELSE ObValue.BadArgType(2, "text", self.name, opCode.name, loc); END;
            rd1 := fileSys1.remote.OpenRead(text1);
            RETURN NEW(ValRd, what:="<'" & text1 & "' reader>", 
                       picklable:=FALSE, rd:=rd1);
        | RdCode.GetChar => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            IF Rd.Closed(rd1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            RETURN NEW(ObValue.ValChar, char:=Rd.GetChar(rd1));
        | RdCode.Eof => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            IF Rd.Closed(rd1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
	    IF Rd.EOF(rd1) THEN RETURN true ELSE RETURN false END;
        | RdCode.UnGetChar => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            IF Rd.Closed(rd1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            Rd.UnGetChar(rd1);
            RETURN ObValue.valOk;
        | RdCode.CharsReady => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            IF Rd.Closed(rd1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            RETURN NEW(ObValue.ValInt, int:=Rd.CharsReady(rd1), temp:=temp);
        | RdCode.GetText => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(2, "int", self.name, opCode.name, loc); END;
            IF Rd.Closed(rd1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            IF int1<0 THEN
              ObValue.BadArgVal(2, "non-negative", self.name, opCode.name, loc);
            END;
            RETURN ObValue.NewText(Rd.GetText(rd1, int1));
        | RdCode.GetLine => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            IF Rd.Closed(rd1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            RETURN ObValue.NewText(Rd.GetLine(rd1));
        | RdCode.Index => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            IF Rd.Closed(rd1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            RETURN NEW(ObValue.ValInt, int:=Rd.Index(rd1), temp:=temp);
        | RdCode.Length => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            IF Rd.Closed(rd1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            IF Rd.Intermittent(rd1) THEN
              ObValue.BadArgVal(1,"non-intermittent",self.name,opCode.name,loc);
            END;
            RETURN NEW(ObValue.ValInt, int:=Rd.Length(rd1), temp:=temp);
        | RdCode.Seek => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(2, "int", self.name, opCode.name, loc); END;
            IF Rd.Closed(rd1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            IF NOT Rd.Seekable(rd1) THEN
              ObValue.BadArgVal(1, "seekable", self.name, opCode.name, loc);
            END;
            IF int1<0 THEN
              ObValue.BadArgVal(2, "non-negative", self.name, opCode.name, loc);
            END;
            Rd.Seek(rd1, int1);
            RETURN ObValue.valOk;
        | RdCode.Close => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            Rd.Close(rd1);
            RETURN ObValue.valOk;
        | RdCode.Intermittent => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
	    IF Rd.Intermittent(rd1) THEN RETURN true ELSE RETURN false END;
        | RdCode.Seekable => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
	    IF Rd.Seekable(rd1) THEN RETURN true ELSE RETURN false END;
        | RdCode.Closed => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
	    IF Rd.Closed(rd1) THEN RETURN true ELSE RETURN false END;
        ELSE
          ObValue.BadOp(self.name, opCode.name, loc);
        END;
      EXCEPT
      | Rd.Failure, ObValue.ServerError =>
          ObValue.RaiseException(rdFailureException, 
                               self.name & "_" & opCode.name, loc);
      | Rd.EndOfFile => 
          ObValue.RaiseException(rdEofFailureException,
                               self.name & "_" & opCode.name, loc);
      | Thread.Alerted =>
          ObValue.RaiseException(ObValue.threadAlerted, 
                               self.name&"_"&opCode.name, loc);
      | NetObj.Error(atoms) =>
          ObValue.RaiseNetException(self.name&"_"&opCode.name, atoms, loc);
      END;
    END EvalRd;

(* ============ "wr" package ============ *)

TYPE

  WrCode = {Failure, New, Stdout, Stderr, Open, OpenAppend, ToText, PutChar, 
            PutText, Flush, Index, Length, Seek, Close, Buffered,
            Seekable, Closed};

  WrOpCode =  
    ObLib.OpCode OBJECT
        code: WrCode;
      END;
    
  PackageWr = 
    ObLib.T OBJECT
      OVERRIDES
        Eval:=EvalWr;
      END;

  PROCEDURE IsWr(self: ValWr; other: ObValue.ValAnything): BOOLEAN =
  BEGIN
    TYPECASE other OF ValWr(oth)=> RETURN self.wr = oth.wr;
    ELSE RETURN FALSE END;
  END IsWr;

  PROCEDURE CopyWr(self: ObValue.ValAnything; tbl: ObValue.Tbl;
    loc: SynLocation.T): ObValue.ValAnything RAISES {ObValue.Error} =
  BEGIN
    RETURN self;
  END CopyWr;

  VAR wrFailureException: ObValue.ValException;

  PROCEDURE NewWrOC(name: TEXT; arity: INTEGER; code: WrCode)
    : WrOpCode =
  BEGIN
    RETURN NEW(WrOpCode, name:=name, arity:=arity, code:=code);
  END NewWrOC;

  PROCEDURE SetupWr() =
  TYPE OpCodes = ARRAY OF ObLib.OpCode;
  VAR opCodes: REF OpCodes;
  BEGIN
    opCodes := NEW(REF OpCodes, NUMBER(WrCode));
    opCodes^ :=
      OpCodes{
      NewWrOC("failure", -1, WrCode.Failure), 
      NewWrOC("new", 0, WrCode.New), 
      NewWrOC("stdout", -1, WrCode.Stdout),
      NewWrOC("stderr", -1, WrCode.Stderr),
      NewWrOC("open", 2, WrCode.Open),
      NewWrOC("openAppend", 2, WrCode.OpenAppend),
      NewWrOC("toText", 1, WrCode.ToText), 
      NewWrOC("putChar", 2, WrCode.PutChar), 
      NewWrOC("putText", 2, WrCode.PutText), 
      NewWrOC("flush", 1, WrCode.Flush), 
      NewWrOC("index", 1, WrCode.Index), 
      NewWrOC("length", 1, WrCode.Length), 
      NewWrOC("seek", 2, WrCode.Seek), 
      NewWrOC("close", 1, WrCode.Close), 
      NewWrOC("buffered", 1, WrCode.Buffered), 
      NewWrOC("seekable", 1, WrCode.Seekable), 
      NewWrOC("closed", 1, WrCode.Closed)
      };
    ObLib.Register(
      NEW(PackageWr, name:="wr", opCodes:=opCodes));
    wrFailureException := NEW(ObValue.ValException, name:="wr_failure");
  END SetupWr;

  PROCEDURE EvalWr(self: PackageWr; opCode: ObLib.OpCode; 
      arity: ObLib.OpArity; READONLY args: ObValue.ArgArray; 
      temp: BOOLEAN; loc: SynLocation.T)
      : ObValue.Val RAISES {ObValue.Error, ObValue.Exception} =
    VAR wr1: Wr.T; text1: TEXT; char1: CHAR; int1: INTEGER;
      fileSys1: ObValue.ValFileSystem;
    BEGIN
      TRY
        CASE NARROW(opCode, WrOpCode).code OF
        | WrCode.Failure => 
            RETURN wrFailureException;
        | WrCode.New =>
            wr1 := TextWr.New();
            RETURN NEW(ValWr, what:="<a writer>", 
                picklable:=FALSE, wr:=wr1);
        | WrCode.Stdout => 
            RETURN NEW(ValWr, what:="<stdout writer>", 
              picklable:=FALSE, wr:=Stdio.stdout);
        | WrCode.Stderr => 
            RETURN NEW(ValWr, what:="<stderr writer>", 
              picklable:=FALSE, wr:=Stdio.stderr);
        | WrCode.Open => 
            TYPECASE args[1] OF | ObValue.ValFileSystem(node) => fileSys1:=node;
            ELSE ObValue.BadArgType(1, "file system", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValText(node) => text1:=node.text;
            ELSE ObValue.BadArgType(2, "text", self.name, opCode.name, loc); END;
            wr1 := fileSys1.remote.OpenWrite(text1);
            RETURN NEW(ValWr, what:="<'" & text1 & "' writer>", 
                       picklable:=FALSE, wr:=wr1);
        | WrCode.OpenAppend => 
            TYPECASE args[1] OF | ObValue.ValFileSystem(node) => fileSys1:=node;
            ELSE ObValue.BadArgType(1, "file system", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValText(node) => text1:=node.text;
            ELSE ObValue.BadArgType(2, "text", self.name, opCode.name, loc); END;
            wr1 := fileSys1.remote.OpenAppend(text1);
            RETURN NEW(ValWr, what:="<'" & text1 & "' writer>", 
                       picklable:=FALSE, wr:=wr1);
       | WrCode.ToText =>
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
            TYPECASE wr1 OF
            | TextWr.T(wr) =>
                RETURN ObValue.NewText(TextWr.ToText(wr));
            ELSE
              ObValue.BadArgVal(1, "locally produced by wr_new", self.name, 
                              opCode.name, loc);
            END;
        | WrCode.PutChar => 
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValChar(node) => char1 := node.char;
            ELSE ObValue.BadArgType(2, "char", self.name, opCode.name, loc); END;
            IF Wr.Closed(wr1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            Wr.PutChar(wr1, char1);
            RETURN ObValue.valOk;
        | WrCode.PutText => 
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValText(node) => text1:=node.text;
            ELSE ObValue.BadArgType(2, "text", self.name, opCode.name, loc); END;
            IF Wr.Closed(wr1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            Wr.PutText(wr1, text1);
            RETURN ObValue.valOk;
        | WrCode.Flush => 
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
            IF Wr.Closed(wr1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            Wr.Flush(wr1);
            RETURN ObValue.valOk;
        | WrCode.Index => 
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
            IF Wr.Closed(wr1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            RETURN NEW(ObValue.ValInt, int:=Wr.Index(wr1), temp:=temp);
        | WrCode.Length => 
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
            IF Wr.Closed(wr1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            RETURN NEW(ObValue.ValInt, int:=Wr.Length(wr1), temp:=temp);
        | WrCode.Seek => 
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(2, "int", self.name, opCode.name, loc); END;
            IF Wr.Closed(wr1) THEN
              ObValue.BadArgVal(1, "non-closed", self.name, opCode.name, loc);
            END;
            IF NOT Wr.Seekable(wr1) THEN
              ObValue.BadArgVal(1, "seekable", self.name, opCode.name, loc);
            END;
            IF int1<0 THEN
              ObValue.BadArgVal(2, "non-negative", self.name, opCode.name, loc);
            END;
            Wr.Seek(wr1, int1);
            RETURN ObValue.valOk;
        | WrCode.Close => 
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
            Wr.Close(wr1);
            RETURN ObValue.valOk;
        | WrCode.Buffered => 
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
	    IF Wr.Buffered(wr1) THEN RETURN true ELSE RETURN false END;
        | WrCode.Seekable => 
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
	    IF Wr.Seekable(wr1) THEN RETURN true ELSE RETURN false END;
        | WrCode.Closed => 
            TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
            ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
	    IF Wr.Closed(wr1) THEN RETURN true ELSE RETURN false END;
        ELSE
          ObValue.BadOp(self.name, opCode.name, loc);
        END;
      EXCEPT
      | Wr.Failure, ObValue.ServerError =>
          ObValue.RaiseException(wrFailureException, 
                               self.name & "_" & opCode.name, loc);
      | Thread.Alerted =>
          ObValue.RaiseException(ObValue.threadAlerted, 
                               self.name&"_"&opCode.name, loc);
      | NetObj.Error(atoms) =>
          ObValue.RaiseNetException(self.name&"_"&opCode.name, atoms, loc);
      END;
    END EvalWr;

(* ============ "lex" package ============ *)

TYPE

  LexCode = {Failure, Scan, Skip, Match, Bool, Int, Real};

  LexOpCode =  
    ObLib.OpCode OBJECT
        code: LexCode;
      END;
    
  PackageLex = 
    ObLib.T OBJECT
      OVERRIDES
        Eval:=EvalLex;
      END;

  VAR lexFailureException: ObValue.ValException;

  PROCEDURE NewLexOC(name: TEXT; arity: INTEGER; code: LexCode)
    : LexOpCode =
  BEGIN
    RETURN NEW(LexOpCode, name:=name, arity:=arity, code:=code);
  END NewLexOC;

  PROCEDURE SetupLex() =
  TYPE OpCodes = ARRAY OF ObLib.OpCode;
  VAR opCodes: REF OpCodes;
  BEGIN
    opCodes := NEW(REF OpCodes, NUMBER(LexCode));
    opCodes^ :=
      OpCodes{
      NewLexOC("failure", -1, LexCode.Failure),
      NewLexOC("scan", 2, LexCode.Scan),
      NewLexOC("skip", 2, LexCode.Skip),
      NewLexOC("match", 2, LexCode.Match),
      NewLexOC("bool", 1, LexCode.Bool),
      NewLexOC("int", 1, LexCode.Int),
      NewLexOC("real", 1, LexCode.Real)
      };
    ObLib.Register(
      NEW(PackageLex, name:="lex", opCodes:=opCodes));
    lexFailureException := NEW(ObValue.ValException, name:="lex_failure");
  END SetupLex;

  PROCEDURE EvalLex(self: PackageLex; opCode: ObLib.OpCode; 
      arity: ObLib.OpArity; READONLY args: ObValue.ArgArray; 
      temp: BOOLEAN; loc: SynLocation.T)
      : ObValue.Val RAISES {ObValue.Error, ObValue.Exception} =
    VAR text1: TEXT; rd1: Rd.T;
    BEGIN
      TRY
        CASE NARROW(opCode, LexOpCode).code OF
        | LexCode.Failure => 
            RETURN lexFailureException;
        | LexCode.Scan => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValText(node) => text1:=node.text;
            ELSE ObValue.BadArgType(2, "text", self.name, opCode.name, loc) END;
            RETURN ObValue.NewText(Lex.Scan(rd1, CharSet(text1)));
        | LexCode.Skip => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValText(node) => text1:=node.text;
            ELSE ObValue.BadArgType(2, "text", self.name, opCode.name, loc) END;
            Lex.Skip(rd1, CharSet(text1));
            RETURN ObValue.valOk;
        | LexCode.Match => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValText(node) => text1:=node.text;
            ELSE ObValue.BadArgType(2, "text", self.name, opCode.name, loc) END;
            Lex.Match(rd1, text1);
            RETURN ObValue.valOk;
        | LexCode.Bool => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
	    IF Lex.Bool(rd1) THEN RETURN true ELSE RETURN false END;
        | LexCode.Int => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            RETURN NEW(ObValue.ValInt, int:=Lex.Int(rd1, 10), temp:=temp);
        | LexCode.Real => 
            TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
            ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
            RETURN NEW(ObValue.ValReal, real:=Lex.LongReal(rd1), temp:=temp);
        ELSE
          ObValue.BadOp(self.name, opCode.name, loc);
        END;
      EXCEPT 
      | Lex.Error, Convert.Failed, FloatMode.Trap =>
          ObValue.RaiseException(lexFailureException, 
                               self.name & "_" & opCode.name, loc);
      | Rd.Failure =>
          ObValue.RaiseException(rdFailureException, 
                               self.name & "_" & opCode.name, loc);
      | Thread.Alerted =>
          ObValue.RaiseException(ObValue.threadAlerted, 
                               self.name&"_"&opCode.name, loc);
      END;
    END EvalLex;

PROCEDURE CharSet(text: TEXT): SET OF CHAR =
  VAR s: SET OF CHAR;
  BEGIN
    s := SET OF CHAR{};
    FOR i:=0 TO Text.Length(text)-1 DO
      s := s + SET OF CHAR{Text.GetChar(text,i)};
    END;
    RETURN s;
 END CharSet;
    
(* ============ "fmt" package ============ *)

TYPE

  FmtCode = {PadLft, PadRht, Bool, Int, Real};

  FmtOpCode =  
    ObLib.OpCode OBJECT
        code: FmtCode;
      END;
    
  PackageFmt = 
    ObLib.T OBJECT
      OVERRIDES
        Eval:=EvalFmt;
      END;

  PROCEDURE NewFmtOC(name: TEXT; arity: INTEGER; code: FmtCode)
    : FmtOpCode =
  BEGIN
    RETURN NEW(FmtOpCode, name:=name, arity:=arity, code:=code);
  END NewFmtOC;

  PROCEDURE SetupFmt() =
  TYPE OpCodes = ARRAY OF ObLib.OpCode;
  VAR opCodes: REF OpCodes;
  BEGIN
    opCodes := NEW(REF OpCodes, NUMBER(FmtCode));
    opCodes^ :=
      OpCodes{
      NewFmtOC("padLft", 2, FmtCode.PadLft),
      NewFmtOC("padRht", 2, FmtCode.PadRht),
      NewFmtOC("bool", 1, FmtCode.Bool),
      NewFmtOC("int", 1, FmtCode.Int),
      NewFmtOC("real", 1, FmtCode.Real)
      };
    ObLib.Register(
      NEW(PackageFmt, name:="fmt", opCodes:=opCodes));
  END SetupFmt;

  PROCEDURE EvalFmt(self: PackageFmt; opCode: ObLib.OpCode; 
      arity: ObLib.OpArity; READONLY args: ObValue.ArgArray; 
      temp: BOOLEAN; loc: SynLocation.T)
      : ObValue.Val RAISES {ObValue.Error, ObValue.Exception} =
    VAR text1: TEXT; bool1: BOOLEAN; int1: INTEGER; real1: LONGREAL;
    BEGIN
        CASE NARROW(opCode, FmtOpCode).code OF
        | FmtCode.PadLft => 
            TYPECASE args[1] OF | ObValue.ValText(node) => text1 := node.text;
            ELSE ObValue.BadArgType(1, "text", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(2, "int", self.name, opCode.name, loc); END;
            RETURN ObValue.NewText(Fmt.Pad(text1, int1, ' ', Fmt.Align.Left));
        | FmtCode.PadRht => 
            TYPECASE args[1] OF | ObValue.ValText(node) => text1 := node.text;
            ELSE ObValue.BadArgType(1, "text", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(2, "int", self.name, opCode.name, loc); END;
            RETURN ObValue.NewText(Fmt.Pad(text1, int1, ' ', Fmt.Align.Right));
        | FmtCode.Bool => 
            TYPECASE args[1] OF | ObValue.ValBool(node) => bool1 := node.bool;
            ELSE ObValue.BadArgType(1, "bool", self.name, opCode.name, loc); END;
            IF bool1 THEN RETURN ObValue.NewText("true");
	    ELSE RETURN ObValue.NewText("false"); END;
        | FmtCode.Int => 
            TYPECASE args[1] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(1, "int", self.name, opCode.name, loc); END;
            RETURN ObValue.NewText(Fmt.Int(int1));
        | FmtCode.Real => 
            TYPECASE args[1] OF | ObValue.ValReal(node) => real1 := node.real;
            ELSE ObValue.BadArgType(1, "real", self.name, opCode.name, loc); END;
            RETURN ObValue.NewText(Fmt.LongReal(real1, literal:=TRUE));
        ELSE
          ObValue.BadOp(self.name, opCode.name, loc);
        END;
    END EvalFmt;

(* ============ "word" package ============ *)

TYPE

  WordCode = {Not, And, Or, Xor, Shift, Rotate};

  WordOpCode =  
    ObLib.OpCode OBJECT
        code: WordCode;
      END;
    
  PackageWord = 
    ObLib.T OBJECT
      OVERRIDES
        Eval:=EvalWord;
      END;

  PROCEDURE NewWordOC(name: TEXT; arity: INTEGER; code: WordCode)
    : WordOpCode =
  BEGIN
    RETURN NEW(WordOpCode, name:=name, arity:=arity, code:=code);
  END NewWordOC;

  PROCEDURE SetupWord() =
  TYPE OpCodes = ARRAY OF ObLib.OpCode;
  VAR opCodes: REF OpCodes;
  BEGIN
    opCodes := NEW(REF OpCodes, NUMBER(WordCode));
    opCodes^ :=
      OpCodes{
      NewWordOC("bitnot",    1, WordCode.Not),
      NewWordOC("bitand",    2, WordCode.And),
      NewWordOC("bitor",     2, WordCode.Or),
      NewWordOC("bitxor",    2, WordCode.Xor),
      NewWordOC("bitshift",  2, WordCode.Shift),
      NewWordOC("bitrotate", 2, WordCode.Rotate)
      };
    ObLib.Register(
      NEW(PackageWord, name:="word", opCodes:=opCodes));
  END SetupWord;

  PROCEDURE EvalWord(self: PackageWord; opCode: ObLib.OpCode; 
      arity: ObLib.OpArity; READONLY args: ObValue.ArgArray; 
      temp: BOOLEAN; loc: SynLocation.T)
      : ObValue.Val RAISES {ObValue.Error, ObValue.Exception} =
    VAR int1, int2: INTEGER;
    BEGIN
        CASE NARROW(opCode, WordOpCode).code OF
        | WordCode.Not => 
            TYPECASE args[1] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(1, "int", self.name, opCode.name, loc); END;
            RETURN Obliq.NewInt(Word.Not (int1));
        | WordCode.And => 
            TYPECASE args[1] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(1, "int", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValInt(node) => int2 := node.int;
            ELSE ObValue.BadArgType(2, "int", self.name, opCode.name, loc); END;
            RETURN Obliq.NewInt(Word.And (int1, int2));
        | WordCode.Or => 
            TYPECASE args[1] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(1, "int", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValInt(node) => int2 := node.int;
            ELSE ObValue.BadArgType(2, "int", self.name, opCode.name, loc); END;
            RETURN Obliq.NewInt(Word.Or (int1, int2));
        | WordCode.Xor => 
            TYPECASE args[1] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(1, "int", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValInt(node) => int2 := node.int;
            ELSE ObValue.BadArgType(2, "int", self.name, opCode.name, loc); END;
            RETURN Obliq.NewInt(Word.Xor (int1, int2));
        | WordCode.Shift => 
            TYPECASE args[1] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(1, "int", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValInt(node) => int2 := node.int;
            ELSE ObValue.BadArgType(2, "int", self.name, opCode.name, loc); END;
            RETURN Obliq.NewInt(Word.Shift (int1, int2));
        | WordCode.Rotate => 
            TYPECASE args[1] OF | ObValue.ValInt(node) => int1 := node.int;
            ELSE ObValue.BadArgType(1, "int", self.name, opCode.name, loc); END;
            TYPECASE args[2] OF | ObValue.ValInt(node) => int2 := node.int;
            ELSE ObValue.BadArgType(2, "int", self.name, opCode.name, loc); END;
            RETURN Obliq.NewInt(Word.Rotate (int1, int2));
        ELSE
          ObValue.BadOp(self.name, opCode.name, loc);
        END;
    END EvalWord;


(* ============ "pickle" package ============ *)

CONST CurrentPickleVersion = 1;
TYPE PickleVersion = 
  BRANDED "ObliqPickleVersion" OBJECT version: INTEGER END;

TYPE

  PickleCode = {Failure, Write, Read};

  PickleOpCode =  
    ObLib.OpCode OBJECT
        code: PickleCode;
      END;
    
  PackagePickle = 
    ObLib.T OBJECT
      OVERRIDES
        Eval:=EvalPickle;
      END;

  VAR pickleFailureException: ObValue.ValException;

  PROCEDURE NewPickleOC(name: TEXT; arity: INTEGER; code: PickleCode)
    : PickleOpCode =
  BEGIN
    RETURN NEW(PickleOpCode, name:=name, arity:=arity, code:=code);
  END NewPickleOC;

  PROCEDURE SetupPickle() =
  TYPE OpCodes = ARRAY OF ObLib.OpCode;
  VAR opCodes: REF OpCodes;
  BEGIN
    opCodes := NEW(REF OpCodes, NUMBER(PickleCode));
    opCodes^ :=
      OpCodes{
      NewPickleOC("failure", -1, PickleCode.Failure),
      NewPickleOC("write", 2, PickleCode.Write),
      NewPickleOC("read", 1, PickleCode.Read)
      };
    ObLib.Register(
      NEW(PackagePickle, name:="pickle", opCodes:=opCodes));
    pickleFailureException := NEW(ObValue.ValException, name:="pickle_failure");
  END SetupPickle;

  PROCEDURE EvalPickle(self: PackagePickle; opCode: ObLib.OpCode; 
      arity: ObLib.OpArity; READONLY args: ObValue.ArgArray; 
      temp: BOOLEAN; loc: SynLocation.T)
      : ObValue.Val RAISES {ObValue.Error, ObValue.Exception} =
    VAR wr1: Wr.T; rd1: Rd.T;
    BEGIN
      TRY
      CASE NARROW(opCode, PickleOpCode).code OF
      | PickleCode.Failure => 
          RETURN pickleFailureException;
      | PickleCode.Write => 
          TYPECASE args[1] OF | ValWr(node) => wr1 := node.wr;
          ELSE ObValue.BadArgType(1, "wr", self.name, opCode.name, loc); END;
          Pickle.Write(wr1, NEW(PickleVersion, version:=CurrentPickleVersion));
          Pickle.Write(wr1, 
            ObValue.CopyValToLocal(args[2], ObValue.NewTbl(), loc));
          RETURN ObValue.valOk;
      | PickleCode.Read => 
          TYPECASE args[1] OF | ValRd(node) => rd1 := node.rd;
          ELSE ObValue.BadArgType(1, "rd", self.name, opCode.name, loc); END;
          TYPECASE Pickle.Read(rd1) OF
          | PickleVersion(p) =>
            IF p.version # CurrentPickleVersion THEN 
              RAISE Pickle.Error("");
            END;
          ELSE RAISE Pickle.Error("");
          END;
          RETURN 
            ObValue.CopyLocalToVal(Pickle.Read(rd1), ObValue.NewTbl(), loc);
      ELSE
        ObValue.BadOp(self.name, opCode.name, loc);
      END;
      EXCEPT
      | Pickle.Error =>
          ObValue.RaiseException(pickleFailureException, opCode.name, loc);
      | Wr.Failure =>
          ObValue.RaiseException(wrFailureException, opCode.name, loc);
      | Rd.Failure =>
          ObValue.RaiseException(rdFailureException, opCode.name, loc);
      | Rd.EndOfFile => 
          ObValue.RaiseException(rdEofFailureException, opCode.name, loc);
      | Thread.Alerted =>
          ObValue.RaiseException(ObValue.threadAlerted, 
                               self.name&"_"&opCode.name, loc);
      | NetObj.Error(atoms) =>
          ObValue.RaiseNetException(self.name&"_"&opCode.name, atoms, loc);
      END;
    END EvalPickle;

(* ============ "process" package ============ *)

TYPE

  ProcCode = {New, In, Out, Err, Complete, Filter};

  ProcOpCode =  
    ObLib.OpCode OBJECT
        code: ProcCode;
      END;
    
  PackageProc = 
    ObLib.T OBJECT
      OVERRIDES
        Eval:=EvalProc;
      END;

  PROCEDURE IsProc(self: ValProc; other: ObValue.ValAnything): BOOLEAN =
  BEGIN
    TYPECASE other OF ValProc(oth)=> RETURN self.proc = oth.proc;
    ELSE RETURN FALSE END;
  END IsProc;

  PROCEDURE CopyProc(self: ObValue.ValAnything; tbl: ObValue.Tbl;
    loc: SynLocation.T): ObValue.ValAnything RAISES {ObValue.Error} =
  BEGIN
    ObValue.RaiseError("Cannot copy processes", loc);
  END CopyProc;

  PROCEDURE NewProcOC(name: TEXT; arity: INTEGER; code: ProcCode)
    : ProcOpCode =
  BEGIN
    RETURN NEW(ProcOpCode, name:=name, arity:=arity, code:=code);
  END NewProcOC;

  PROCEDURE SetupProc() =
  TYPE OpCodes = ARRAY OF ObLib.OpCode;
  VAR opCodes: REF OpCodes;
  BEGIN
    opCodes := NEW(REF OpCodes, NUMBER(ProcCode));
    opCodes^ :=
      OpCodes{
      NewProcOC("new", 3, ProcCode.New),
      NewProcOC("in", 1, ProcCode.In),
      NewProcOC("out", 1, ProcCode.Out),
      NewProcOC("err", 1, ProcCode.Err),
      NewProcOC("complete", 1, ProcCode.Complete),
      NewProcOC("filter", 3, ProcCode.Filter)};
    ObLib.Register(
      NEW(PackageProc, name:="process", opCodes:=opCodes));
    ObValue.InhibitTransmission(TYPECODE(ValProc), 
      "processes cannot be transmitted/duplicated");
  END SetupProc;

  PROCEDURE EvalProc(self: PackageProc; opCode: ObLib.OpCode; 
      arity: ObLib.OpArity; READONLY args: ObValue.ArgArray; 
      temp: BOOLEAN; loc: SynLocation.T)
      : ObValue.Val RAISES {ObValue.Error, ObValue.Exception} =
    TYPE Texts = REF ARRAY OF TEXT;
    VAR val: ObValue.Val; text1: TEXT; size, int: INTEGER;
      proc: Process.T; proc1: ValProc; texts: Texts;
      stdinR, stdinW, stdoutR, stdoutW, stderrR, stderrW: Pipe.T;
      stdinWr: FileWr.T; stdoutRd, stderrRd: FileRd.T;
      bool1: BOOLEAN; array1: REF ObValue.Vals;
      processor1: ObValue.ValProcessor;
    BEGIN
      TRY
      CASE NARROW(opCode, ProcOpCode).code OF
      | ProcCode.New => 
          TYPECASE args[1] OF 
          | ObValue.ValProcessor(node) => processor1:=node;
          ELSE ObValue.BadArgType(1, "processor", self.name, opCode.name, loc); END;
          IF processor1 # ObValue.localProcessor THEN
            ObValue.BadArgVal(1, "the local processor", self.name, opCode.name, loc);
          END;
          TYPECASE args[2] OF 
          | ObValue.ValArray(node) => array1:=node.remote.Obtain();
          ELSE ObValue.BadArgType(2, "array", self.name, opCode.name, loc); END;
          TYPECASE args[3] OF | ObValue.ValBool(node) => bool1:=node.bool;
          ELSE ObValue.BadArgType(3, "bool", self.name, opCode.name, loc); END;
          size := NUMBER(array1^);
          IF size=0 THEN
            ObValue.BadArgVal(2, "non-empty", self.name, opCode.name, loc);
          END;
          texts := NEW(Texts, size);
          FOR i := 0 TO size-1 DO
            TYPECASE array1^[i] OF
            | ObValue.ValText(node) => texts^[i] := node.text;
            ELSE 
              ObValue.BadArgType(2, "array(text)", self.name, opCode.name,loc);
            END;
          END;
          Pipe.Open((*out*)stdinR, (*out*)stdinW);
          Pipe.Open((*out*)stdoutR, (*out*)stdoutW);      
          IF bool1 THEN 
            stderrR := stdoutR; stderrW := stdoutW;
          ELSE
            Pipe.Open((*out*)stderrR, (*out*)stderrW);
          END;  
          proc := 
            Process.Create(texts^[0], 
              SUBARRAY(texts^, 1, NUMBER(texts^)-1),
              NIL, NIL, 
              stdinR, stdoutW, stderrW); 
          stdinR.close();
          stdoutW.close();
          IF NOT bool1 THEN stderrW.close(); END;
          stdinWr := NEW(FileWr.T).init(stdinW);
          stdoutRd := NEW(FileRd.T).init(stdoutR);
          IF bool1 THEN
            stderrRd := stdoutRd;
          ELSE
            stderrRd := NEW(FileRd.T).init(stderrR);
          END;
          RETURN NEW(ValProc, what:="<a process>",  picklable:=FALSE, 
            proc:=proc,
            in := 
              NEW(ValWr, what:="<a process stdin writer>", picklable:=FALSE, 
                wr:=stdinWr), 
            out := 
              NEW(ValRd, what:="<a process stdout reader>", picklable:=FALSE, 
                rd := stdoutRd),
            err := 
              NEW(ValRd, what:="<a process stderr reader>", picklable:=FALSE, 
                rd := stderrRd));
      | ProcCode.In => 
          TYPECASE args[1] OF | ValProc(node) => proc1 := node;
          ELSE ObValue.BadArgType(1, "process", self.name, opCode.name, loc); 
          END;
          RETURN proc1.in;
      | ProcCode.Out => 
          TYPECASE args[1] OF | ValProc(node) => proc1 := node;
          ELSE ObValue.BadArgType(1, "process", self.name, opCode.name, loc); 
          END;
          RETURN proc1.out;
      | ProcCode.Err => 
          TYPECASE args[1] OF | ValProc(node) => proc1 := node;
          ELSE ObValue.BadArgType(1, "process", self.name, opCode.name, loc); 
          END;
          RETURN proc1.err;
      | ProcCode.Complete => 
          TYPECASE args[1] OF | ValProc(node) => proc1 := node;
          ELSE ObValue.BadArgType(1, "process", self.name, opCode.name, loc); 
          END;
          int := Process.Wait(proc1.proc);
          Wr.Close(proc1.in.wr);
          Rd.Close(proc1.out.rd);
          Rd.Close(proc1.err.rd);
          RETURN NEW(ObValue.ValInt, int := int, temp:=temp);
      | ProcCode.Filter => 
          TYPECASE args[1] OF 
          | ObValue.ValProcessor(node) => processor1:=node;
          ELSE ObValue.BadArgType(1, "processor", self.name, opCode.name, loc); END;
          IF processor1 # ObValue.localProcessor THEN
            ObValue.BadArgVal(1, "the local processor", self.name, opCode.name, loc);
          END;
          TYPECASE args[2] OF 
          | ObValue.ValArray(node) => array1:=node.remote.Obtain();
          ELSE ObValue.BadArgType(2, "array", self.name, opCode.name, loc); END;
          TYPECASE args[3] OF | ObValue.ValText(node) => text1:=node.text;
          ELSE ObValue.BadArgType(3, "text", self.name, opCode.name, loc); END;
          size := NUMBER(array1^);
          IF size=0 THEN
            ObValue.BadArgVal(2, "non-empty", self.name, opCode.name, loc);
          END;
          texts := NEW(Texts, size);
          FOR i := 0 TO size-1 DO
            TYPECASE array1^[i] OF
            | ObValue.ValText(node) => texts^[i] := node.text;
            ELSE ObValue.BadArgType(2, "array(text)", self.name, 
                   opCode.name, loc);
            END;
          END;
          Pipe.Open((*out*)stdinR, (*out*)stdinW);
          Pipe.Open((*out*)stdoutR, (*out*)stdoutW);      
          proc := 
            Process.Create(texts^[0], 
              SUBARRAY(texts^, 1, NUMBER(texts^)-1),
              NIL, NIL, 
              stdinR, stdoutW, stdoutW); 
          stdinR.close();
          stdoutW.close();
          stdinWr := NEW(FileWr.T).init(stdinW);
          stdoutRd := NEW(FileRd.T).init(stdoutR);
          Wr.PutText(stdinWr, text1);
          Wr.Close(stdinWr);
          val := ObValue.NewText(Rd.GetText(stdoutRd, LAST(CARDINAL)));
          Rd.Close(stdoutRd);
          EVAL Process.Wait(proc);
          RETURN val;
 (* -- LocalPipe
      | ProcCode.Pipe => 
          TYPECASE args[1] OF | ObValue.ValBool(node) => bool1:=node.bool;
          ELSE ObValue.BadArgType(1, "bool", self.name, opCode.name, loc); END;
          rd1 := NEW(LocalPipe.Reaader);
          wr1 := NEW(LocalPipe.Writer);
          LocalPipe.Init(rd1, wr1, NOT bool1);
          RETURN ObValue.NewObject(
            NEW(ObValue.ObjFieldValue, 
              label:="r",
              val:=NEW(ValRd, what:="<a pipe reader>", picklable:=FALSE, 
                  rd:=rd1),
              rest:=NEW(ObValue.ObjFieldValue(
                label:="w",
                val:=NEW(ValWr, what:="<a pipe writer>", picklable:=FALSE, 
                  wr:=wr1),
                rest:=NIL)));
            );
 *)
       ELSE
        ObValue.BadOp(self.name, opCode.name, loc);
      END;
      EXCEPT
      | Rd.Failure, Wr.Failure, Thread.Alerted, OSError.E =>
          ObValue.RaiseError(self.name&"_"&opCode.name, loc);
      | NetObj.Error(atoms) =>
              ObValue.RaiseNetException(
                               self.name&"_"&opCode.name, atoms, loc);
      END;
    END EvalProc;

BEGIN
END ObLibM3.
