m3tools/src/M3Builtin.m3


 Copyright 1996-2000 Critical Mass, Inc. All rights reserved.    
 See file COPYRIGHT-CMASS for details. 

MODULE M3Builtin;

IMPORT M3Const, M3Type, Target, TInt, TFloat;

TYPE
  Desc = RECORD
    proc     : Proc;
    min_args : CARDINAL;
    max_args : CARDINAL;
    is_const : BOOLEAN;
  END;

CONST
  PDesc = ARRAY Proc OF Desc {
    Desc{Proc.Abs,             1, 1, TRUE },
    Desc{Proc.Adr,             1, 1, FALSE },
    Desc{Proc.AdrSize,         1, 1, TRUE },
    Desc{Proc.BitSize,         1, 1, TRUE },
    Desc{Proc.ByteSize,        1, 1, TRUE },
    Desc{Proc.Ceiling,         1, 1, TRUE },
    Desc{Proc.Dec,             1, 2, FALSE },
    Desc{Proc.Dispose,         1, 1, FALSE },
    Desc{Proc.First,           1, 1, TRUE },
    Desc{Proc.Float,           1, 2, TRUE },
    Desc{Proc.Floor,           1, 1, TRUE },
    Desc{Proc.Inc,             1, 2, FALSE },
    Desc{Proc.IsType,          2, 2, TRUE },
    Desc{Proc.Last,            1, 1, TRUE },
    Desc{Proc.Loophole,        2, 2, FALSE },
    Desc{Proc.Max,             2, 2, TRUE },
    Desc{Proc.Min,             2, 2, TRUE },
    Desc{Proc.Narrow,          2, 2, TRUE },
    Desc{Proc.New,             1, 99999, FALSE },
    Desc{Proc.Number,          1, 1, TRUE },
    Desc{Proc.Ord,             1, 1, TRUE },
    Desc{Proc.Round,           1, 1, TRUE },
    Desc{Proc.Subarray,        3, 3, TRUE },
    Desc{Proc.Trunc,           1, 1, TRUE },
    Desc{Proc.Typecode,        1, 1, FALSE },
    Desc{Proc.Val,             2, 2, TRUE },
    Desc{Proc.WordPlus,        2, 2, TRUE },
    Desc{Proc.WordTimes,       2, 2, TRUE },
    Desc{Proc.WordMinus,       2, 2, TRUE },
    Desc{Proc.WordDivide,      2, 2, TRUE },
    Desc{Proc.WordMod,         2, 2, TRUE },
    Desc{Proc.WordLT,          2, 2, TRUE },
    Desc{Proc.WordLE,          2, 2, TRUE },
    Desc{Proc.WordGT,          2, 2, TRUE },
    Desc{Proc.WordGE,          2, 2, TRUE },
    Desc{Proc.WordAnd,         2, 2, TRUE },
    Desc{Proc.WordOr,          2, 2, TRUE },
    Desc{Proc.WordXor,         2, 2, TRUE },
    Desc{Proc.WordNot,         1, 1, TRUE },
    Desc{Proc.WordShift,       2, 2, TRUE },
    Desc{Proc.WordLeftShift,   2, 2, TRUE },
    Desc{Proc.WordRightShift,  2, 2, TRUE },
    Desc{Proc.WordRotate,      2, 2, TRUE },
    Desc{Proc.WordLeftRotate,  2, 2, TRUE },
    Desc{Proc.WordRightRotate, 2, 2, TRUE },
    Desc{Proc.WordExtract,     3, 3, TRUE },
    Desc{Proc.WordInsert,      4, 4, TRUE }
  };

PROCEDURE Eval (p: Proc;  READONLY args: ARRAY OF M3Const.T;
                VAR(*OUT*) val: M3Const.T)
  RAISES {M3Const.Error} =
  VAR n_args := NUMBER (args);  zero: Target.Float;
  BEGIN
    MustBe (PDesc[p].is_const);
    IF (n_args < PDesc[p].min_args) OR (PDesc[p].max_args < n_args) THEN
      RAISE M3Const.Error ("wrong number of arguments");
    END;

    CASE p OF
    | Proc.Abs =>
        WITH z = args[0] DO
          IF z.class = M3Const.Class.Integer THEN
            val.class := z.class;
            val.type  := z.type;
            IF TInt.LT (z.int, TInt.Zero)
              THEN MustBe (TInt.Subtract (TInt.Zero, z.int, val.int));
              ELSE val.int := z.int;
            END;
          ELSIF z.class = M3Const.Class.Float THEN
            IF    (z.float.pre = Target.Precision.Short) THEN zero := TFloat.ZeroR;
            ELSIF (z.float.pre = Target.Precision.Long) THEN  zero := TFloat.ZeroL;
            ELSE                                              zero := TFloat.ZeroX;
            END;
            val.class := z.class;
            val.type  := z.type;
            IF TFloat.LT (z.float, zero)
              THEN MustBe (TFloat.Subtract (zero, z.float, val.float));
              ELSE val.float := z.float;
            END;
          ELSE
            MustBe (FALSE);
          END;
        END;

    | Proc.AdrSize => NotImpl ("ADRSIZE");
    | Proc.BitSize => NotImpl ("BITSIZE");

    | Proc.ByteSize =>
        WITH z = args[0] DO
          IF z.class = M3Const.Class.Type THEN
            VAR info: M3Type.Info; BEGIN
              M3Type.GetInfo (z.type, info);
              val.type  := M3Type.Integer;
              val.class := M3Const.Class.Integer;
              MustBe (info.size >= 0);
              MustBe (TInt.FromInt (info.size, Target.Integer.bytes, val.int));
            END;
          ELSE
            NotImpl ("BYTESIZE(expr)");
          END;
        END;

    | Proc.Ceiling => NotImpl ("CEILING");
    | Proc.First => NotImpl ("FIRST");
    | Proc.Float => NotImpl ("FLOAT");
    | Proc.Floor => NotImpl ("FLOOR");
    | Proc.IsType => NotImpl ("ISTYPE");
    | Proc.Last => NotImpl ("LAST");

    | Proc.Max =>
        WITH a = args[0], b = args[0] DO
          MustBe (a.class = b.class);
          IF a.class = M3Const.Class.Integer THEN
            MustBe (a.type = b.type);
            val.class := a.class;
            val.type  := a.type;
            IF TInt.LT (a.int, b.int)
              THEN val.int := b.int
              ELSE val.int := a.int;
            END;
          ELSIF a.class = M3Const.Class.Enum THEN
            val.class := a.class;
            val.info  := MAX (a.info, b.info);
            val.type  := a.type;
          ELSIF a.class = M3Const.Class.Float THEN
            MustBe (a.float.pre = b.float.pre);
            val.class := a.class;
            val.type  := a.type;
            IF TFloat.LT (a.float, b.float)
              THEN val.float := b.float;
              ELSE val.float := a.float;
            END;
          ELSE
            MustBe (FALSE);
          END;
        END;

    | Proc.Min =>
        WITH a = args[0], b = args[0] DO
          MustBe (a.class = b.class);
          IF a.class = M3Const.Class.Integer THEN
            MustBe (a.type = b.type);
            val.class := a.class;
            val.type  := a.type;
            IF TInt.LT (a.int, b.int)
              THEN val.int := a.int
              ELSE val.int := b.int;
            END;
          ELSIF a.class = M3Const.Class.Enum THEN
            val.class := a.class;
            val.info  := MIN (a.info, b.info);
            val.type  := a.type;
          ELSIF a.class = M3Const.Class.Float THEN
            MustBe (a.float.pre = b.float.pre);
            val.class := a.class;
            val.type  := a.type;
            IF TFloat.LT (a.float, b.float)
              THEN val.float := a.float;
              ELSE val.float := b.float;
            END;
          ELSE
            MustBe (FALSE);
          END;
        END;

    | Proc.Narrow => NotImpl ("NARROW");
    | Proc.Number => NotImpl ("NUMBER");
    | Proc.Ord => NotImpl ("ORD");
    | Proc.Round => NotImpl ("ROUND");
    | Proc.Subarray => NotImpl ("SUBARRAY");
    | Proc.Trunc => NotImpl ("TRUNC");
    | Proc.Val => NotImpl ("VAL");
    | Proc.WordPlus => NotImpl ("Word.Plus");
    | Proc.WordTimes => NotImpl ("Word.Times");
    | Proc.WordMinus => NotImpl ("Word.Minus");
    | Proc.WordDivide => NotImpl ("Word.Divide");
    | Proc.WordMod => NotImpl ("Word.Mod");
    | Proc.WordLT => NotImpl ("Word.LT");
    | Proc.WordLE => NotImpl ("Word.LE");
    | Proc.WordGT => NotImpl ("Word.GT");
    | Proc.WordGE => NotImpl ("Word.GE");
    | Proc.WordAnd => NotImpl ("Word.And");
    | Proc.WordOr => NotImpl ("Word.Or");
    | Proc.WordXor => NotImpl ("Word.Xor");
    | Proc.WordNot => NotImpl ("Word.Not");
    | Proc.WordShift => NotImpl ("Word.Shift");
    | Proc.WordLeftShift => NotImpl ("Word.LeftShift");
    | Proc.WordRightShift => NotImpl ("Word.RightShift");
    | Proc.WordRotate => NotImpl ("Word.Rotate");
    | Proc.WordLeftRotate => NotImpl ("Word.LeftRotate");
    | Proc.WordRightRotate => NotImpl ("Word.RightRotate");
    | Proc.WordExtract => NotImpl ("Word.Extract");
    | Proc.WordInsert => NotImpl ("Word.Insert");
    ELSE <* ASSERT FALSE *>  (* ==> NOT PDesc[i].is_const *)
    END;
  END Eval;

PROCEDURE NotImpl (func: TEXT) RAISES {M3Const.Error} =
  BEGIN
    RAISE M3Const.Error ("cannot evaluate builtin function " & func & " yet.");
  END NotImpl;

PROCEDURE MustBe (b: BOOLEAN) RAISES {M3Const.Error} =
  BEGIN
    IF NOT b THEN RAISE M3Const.Error ("not a constant") END;
  END MustBe;

BEGIN
  FOR p := FIRST (PDesc) TO LAST (PDesc) DO
    <*ASSERT PDesc[p].proc = p *>
  END;
END M3Builtin.