juno-machine/src/JunoValue.m3


 Copyright (C) 1992, Digital Equipment Corporation                         
 All rights reserved.                                                      
 See the file COPYRIGHT for a full description.                            
                                                                           
 Last modified on Thu Mar 28 14:15:55 PST 1996 by heydon                   
      modified on Tue Feb 21 14:22:42 PST 1995 by gnelson                  

UNSAFE MODULE JunoValue; (* for Hash() only *)

IMPORT Math, Formatter, Wr, Pickle, Fmt, ASCII, Text, Word;
IMPORT   FloatMode, RealFloat;

REVEAL
  Null = BRANDED "Juno-NIL" REF RECORD END;

PROCEDURE Unparse(wr: Wr.T; x: T; width, prec: CARDINAL) RAISES {Wr.Failure} =
  VAR f := Formatter.New(wr, width); BEGIN
    UnparseToFmt(f, x, prec);
    Formatter.Flush(f);
    Formatter.Close(f)
  END Unparse;

PROCEDURE UnparseToFmt(f: Formatter.T; x: T; prec: CARDINAL)
  RAISES {Wr.Failure} =

  PROCEDURE C(c: CHAR) RAISES {Wr.Failure} =
    BEGIN
      Formatter.PutChar(f, c)
    END C;

  PROCEDURE S(t: TEXT) RAISES {Wr.Failure} =
    BEGIN
      Formatter.PutText(f, t, raw := TRUE)
    END S;

  PROCEDURE UnpReal(r: Real) RAISES {Wr.Failure} =
    BEGIN
      Formatter.PutText(f, Fmt.Real(r, prec := prec), raw := TRUE)
    END UnpReal;

  PROCEDURE Octal(c: CHAR) RAISES {Wr.Failure} =
    BEGIN
      C('\\');
      S(Fmt.Pad(Fmt.Int(ORD(c), base := 8), length := 3, padChar := '0'))
    END Octal;

  PROCEDURE UnpText(t: TEXT) RAISES {Wr.Failure} =
    BEGIN
      C('"');
      FOR i := 0 TO Text.Length(t) - 1 DO
        VAR c := Text.GetChar(t, i); BEGIN
          IF c IN ASCII.Asciis THEN
            IF c IN ASCII.Controls THEN
              CASE c OF
                '\n' => C('\\'); C('n')
              | '\t' => C('\\'); C('t')
              | '\r' => C('\\'); C('r')
              | '\f' => C('\\'); C('f')
              ELSE Octal(c)
              END
            ELSE
              CASE c OF
              | '\\' => C('\\'); C(c)
              | '\"' => C('\\'); C(c)
              ELSE C(c)
              END
            END
          ELSE
            Octal(c)
          END
        END
      END;
      C('"')
    END UnpText;

  PROCEDURE UnpList(p: REF Pair) RAISES {Wr.Failure} =
    BEGIN
      C('[');
      Formatter.Begin(f);
      LOOP
        UnpValue(p.car);
        IF p.cdr = Nil THEN EXIT END;
        p := NARROW(p.cdr, REF Pair);
        S(", ");
        Formatter.UnitedBreak(f, 1)
      END;
      Formatter.End(f);
      C(']')
    END UnpList;

  PROCEDURE UnpPair(p: REF Pair) RAISES {Wr.Failure} =
    BEGIN
      C('(');
      Formatter.Begin(f);
      UnpValue(p.car);
      S(", ");
      Formatter.UnitedBreak(f, 1);
      UnpValue(p.cdr);
      Formatter.End(f);
      C(')')
    END UnpPair;

  PROCEDURE UnpNil() RAISES {Wr.Failure} =
    BEGIN
      Formatter.PutText(f, "NIL", raw := TRUE)
    END UnpNil;

  PROCEDURE UnpValue (x: T) RAISES {Wr.Failure} =
    BEGIN
      TYPECASE x OF <* NOWARN *>
      | NULL => <* ASSERT FALSE *>
      | Null => UnpNil()
      | REF REAL(r) => UnpReal(r^)
      | TEXT(t) => UnpText(t)
      | REF Pair(p) => IF ListLen(p) # -1 THEN UnpList(p) ELSE UnpPair(p) END
      END
    END UnpValue;

  BEGIN
    UnpValue(x)
  END UnparseToFmt;

PROCEDURE Equal(READONLY t1, t2: T): BOOLEAN =
  CONST Epsilon = 1.0E-3; BEGIN
    IF TYPECODE(t1) # TYPECODE(t2) THEN RETURN FALSE END;
    TYPECASE t1 OF <* NOWARN *>
    | Null => RETURN TRUE
    | REF Real(r1) => RETURN ABS(r1^ - NARROW(t2, REF Real)^) < Epsilon
    | TEXT(txt1) => RETURN Text.Equal(txt1, NARROW(t2, TEXT))
    | REF Pair(p1) =>
        VAR p2 := NARROW(t2, REF Pair); BEGIN
          RETURN Equal(p1.car, p2.car) AND Equal(p1.cdr, p2.cdr)
        END
    END
  END Equal;

PROCEDURE Hash(READONLY k: T): Word.T =
  (* This technique for loopholing Real's into Word.T's requires that
     "BITSIZE(Real) <= BITSIZE(Word.T)" *)
  CONST K = BITSIZE(Real); Max = Word.Minus(Word.Shift(1, K-1), 1);
  TYPE RealWord = BITS K FOR [-Max-1..Max];
  BEGIN
    TYPECASE k OF <* NOWARN *>
      Null => RETURN 0
    | TEXT (t) => RETURN Text.Hash(t)
    | REF Pair (p) => RETURN Word.Xor(Hash(p.car), Hash(p.cdr))
    | REF Real (r) => RETURN LOOPHOLE(r^, RealWord)
    END
  END Hash;

PROCEDURE Sin(x: Real): Real =
  BEGIN RETURN FLOAT(Math.sin(FLOAT(x, LONGREAL)), Real) END Sin;

PROCEDURE Cos(x: Real): Real =
  BEGIN RETURN FLOAT(Math.cos(FLOAT(x, LONGREAL)), Real) END Cos;

PROCEDURE Tan(x: Real): Real =
  BEGIN RETURN FLOAT(Math.tan(FLOAT(x, LONGREAL)), Real) END Tan;

PROCEDURE Asin(x: Real): Real =
  BEGIN RETURN FLOAT(Math.asin(FLOAT(x, LONGREAL)), Real) END Asin;

PROCEDURE Acos(x: Real): Real =
  BEGIN RETURN FLOAT(Math.acos(FLOAT(x, LONGREAL)), Real) END Acos;

PROCEDURE Atan(y, x: Real): Real =
  BEGIN
    RETURN FLOAT(Math.atan2(FLOAT(y, LONGREAL), FLOAT(x, LONGREAL)), Real)
  END Atan;

PROCEDURE Exp(x: Real): Real =
  BEGIN RETURN FLOAT(Math.exp(FLOAT(x, LONGREAL)), Real) END Exp;

PROCEDURE Ln(x: Real): Real =
  BEGIN RETURN FLOAT(Math.log(FLOAT(x, LONGREAL)), Real) END Ln;

PROCEDURE Sqrt(x: Real): Real =
  BEGIN RETURN FLOAT(Math.sqrt(FLOAT(x, LONGREAL)), Real) END Sqrt;

PROCEDURE RefReal(x: Real): REF Real =
  VAR res := NEW(REF Real); BEGIN
    res^ := x;
    RETURN res
  END RefReal;

PROCEDURE NewPoint(x, y: Real): REF Pair =
  BEGIN
    RETURN NEW(REF Pair, car := RefReal(x), cdr := RefReal(y))
  END NewPoint;

PROCEDURE ListFromVals(READONLY v: ARRAY OF T): T =
  VAR res: T := Nil; BEGIN
    FOR i := LAST(v) TO FIRST(v) BY -1 DO
      res := NEW(REF Pair, car := v[i], cdr := res)
    END;
    RETURN res
  END ListFromVals;

PROCEDURE IsList(v: T): BOOLEAN =
  BEGIN RETURN ListLen(v) > 0 END IsList;

PROCEDURE ListLen(t: T): INTEGER =
  VAR res := 0; BEGIN
    LOOP
      TYPECASE t OF
      | REF Pair (p) =>
        INC(res);
        t := p.cdr
      ELSE EXIT
      END
    END;
    IF t = Nil
      THEN RETURN res
      ELSE RETURN -1
    END
  END ListLen;

PROCEDURE NullWrite (
    <*UNUSED*> sp: Pickle.Special;
    <*UNUSED*> r: REFANY;
    <*UNUSED*> writer: Pickle.Writer) =
  BEGIN
  END NullWrite;

PROCEDURE NullRead (
    <*UNUSED*> sp: Pickle.Special;
    <*UNUSED*> reader: Pickle.Reader;
    <*UNUSED*> id: Pickle.RefID) : REFANY =
  BEGIN
    RETURN Nil
  END NullRead;

BEGIN
  Nil := NEW(Null);
  Pickle.RegisterSpecial (NEW (Pickle.Special, sc := TYPECODE (Null),
    write := NullWrite,
    read  := NullRead));
  <* FATAL FloatMode.Trap *> BEGIN
    HalfEps := (RealFloat.NextAfter(1.0, 2.0) - 1.0) * 0.5
  END
END JunoValue.

interface JunoValue is in: