(* Ulm's Oberon Library
   Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany
   ----------------------------------------------------------------------------
   Ulm's Oberon Library is free software; you can redistribute it
   and/or modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either version
   2 of the License, or (at your option) any later version.

   Ulm's Oberon Library is distributed in the hope that it will be
   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   ----------------------------------------------------------------------------
   E-mail contact: oberon@mathematik.uni-ulm.de
   ----------------------------------------------------------------------------
   $Id: Operations.om,v 1.4 2004/09/16 18:31:54 borchert Exp $
   ----------------------------------------------------------------------------
   $Log: Operations.om,v $
   Revision 1.4  2004/09/16 18:31:54  borchert
   optimization for Assign added in case of a non-NIL target
   and identical types for target and source

   Revision 1.3  1997/02/05  16:27:45  borchert
   Init asserts now that Services.Init hat been called previously
   for ``op''

   Revision 1.2  1995/01/16  21:39:50  borchert
   - assertions of Assertions have been converted into real assertions
   - some fixes due to changes of PersistentObjects

   Revision 1.1  1994/02/22  20:09:03  borchert
   Initial revision

   ----------------------------------------------------------------------------
   AFB 12/91
   ----------------------------------------------------------------------------
*)

MODULE ulmOperations;

   (* generic support of arithmetic operations *)

   IMPORT Events := ulmEvents, Objects := ulmObjects, PersistentDisciplines := ulmPersistentDisciplines, PersistentObjects := ulmPersistentObjects, Services := ulmServices;

   CONST
      add* = 0; sub* = 1; mul* = 2; div* = 3; cmp* = 4;
   TYPE
      Operation* = SHORTINT; (* add..cmp *)
      Operand* = POINTER TO OperandRec;

   TYPE
      CapabilitySet* = SET; (* SET OF [add..cmp] *)
      CreateProc* = PROCEDURE (VAR op: Operand);
	 (* should call Operations.Init for op *)
      OperatorProc* = PROCEDURE (op: Operation; op1, op2: Operand;
				VAR result: Operand);
      AssignProc* = PROCEDURE (VAR target: Operand; source: Operand);
      CompareProc* = PROCEDURE (op1, op2: Operand) : INTEGER;
      Interface* = POINTER TO InterfaceRec;
      InterfaceRec* =
	 RECORD
	    (Objects.ObjectRec)
	    create*: CreateProc;
	    assign*: AssignProc;
	    op*: OperatorProc;
	    compare*: CompareProc;
	 END;

   TYPE
      OperandRec* =
	 RECORD
	    (PersistentDisciplines.ObjectRec)
	    if: Interface;
	    caps: CapabilitySet;
	 END;
   VAR
      operandType: Services.Type;

   PROCEDURE Init*(op: Operand; if: Interface; caps: CapabilitySet);
      VAR
	 type: Services.Type;
   BEGIN
      Services.GetType(op, type); ASSERT(type # NIL);
      op.if := if; op.caps := caps;
   END Init;

   PROCEDURE Capabilities*(op: Operand) : CapabilitySet;
   BEGIN
      RETURN op.caps
   END Capabilities;

   PROCEDURE Compatible*(op1, op2: Operand) : BOOLEAN;
      (* return TRUE if both operands have the same interface *)
   BEGIN
      RETURN op1.if = op2.if
   END Compatible;

   (* the interface of the first operand must match the interface
      of all other operands;
      the result parameter must be either NIL or already initialized
      with the same interface
   *)

   PROCEDURE Op(op: Operation; op1, op2: Operand; VAR result: Operand);

      VAR
	 tmpresult: Operand;
   BEGIN
      ASSERT(op1.if = op2.if);
      ASSERT(op IN op1.caps);
      (* we are very defensive here because the type of tmpresult
	 is perhaps not identical to result or an extension of it;
	 op1.if.create(result) will not work in all cases
	 because of type guard failures
      *)
      op1.if.create(tmpresult);
      op1.if.op(op, op1, op2, tmpresult);
      result := tmpresult;
   END Op;

   PROCEDURE Add*(op1, op2: Operand) : Operand;
      VAR result: Operand;
   BEGIN
      result := NIL;
      Op(add, op1, op2, result);
      RETURN result
   END Add;

   PROCEDURE Add2*(VAR op1: Operand; op2: Operand);
   BEGIN
      Op(add, op1, op2, op1);
   END Add2;

   PROCEDURE Add3*(VAR result: Operand; op1, op2: Operand);
   BEGIN
      Op(add, op1, op2, result);
   END Add3;

   PROCEDURE Sub*(op1, op2: Operand) : Operand;
      VAR result: Operand;
   BEGIN
      result := NIL;
      Op(sub, op1, op2, result);
      RETURN result
   END Sub;

   PROCEDURE Sub2*(VAR op1: Operand; op2: Operand);
   BEGIN
      Op(sub, op1, op2, op1);
   END Sub2;

   PROCEDURE Sub3*(VAR result: Operand; op1, op2: Operand);
   BEGIN
      Op(sub, op1, op2, result);
   END Sub3;

   PROCEDURE Mul*(op1, op2: Operand) : Operand;
      VAR result: Operand;
   BEGIN
      result := NIL;
      Op(mul, op1, op2, result);
      RETURN result
   END Mul;

   PROCEDURE Mul2*(VAR op1: Operand; op2: Operand);
   BEGIN
      Op(mul, op1, op2, op1);
   END Mul2;

   PROCEDURE Mul3*(VAR result: Operand; op1, op2: Operand);
   BEGIN
      Op(mul, op1, op2, result);
   END Mul3;

   PROCEDURE Div*(op1, op2: Operand) : Operand;
      VAR result: Operand;
   BEGIN
      result := NIL;
      Op(div, op1, op2, result);
      RETURN result
   END Div;

   PROCEDURE Div2*(VAR op1: Operand; op2: Operand);
   BEGIN
      Op(div, op1, op2, op1);
   END Div2;

   PROCEDURE Div3*(VAR result: Operand; op1, op2: Operand);
   BEGIN
      Op(div, op1, op2, result);
   END Div3;

   PROCEDURE Compare*(op1, op2: Operand) : INTEGER;
   BEGIN
      ASSERT(op1.if = op2.if);
      ASSERT(cmp IN op1.caps);
      RETURN op1.if.compare(op1, op2)
   END Compare;

   PROCEDURE Assign*(VAR target: Operand; source: Operand);
      VAR
	 tmpTarget: Operand;
	 typesIdentical: BOOLEAN;
	 targetType, sourceType: Services.Type;
   BEGIN
      IF (target # NIL) & (target.if = source.if) THEN
	 Services.GetType(target, targetType);
	 Services.GetType(source, sourceType);
	 typesIdentical := targetType = sourceType;
      ELSE
	 typesIdentical := FALSE;
      END;
      IF typesIdentical THEN
	 source.if.assign(target, source);
      ELSE
	 source.if.create(tmpTarget);
	 source.if.assign(tmpTarget, source);
	 target := tmpTarget;
      END;
   END Assign;

   PROCEDURE Copy*(source, target: Operand);
   BEGIN
      source.if.assign(target, source);
   END Copy;

BEGIN
   PersistentObjects.RegisterType(operandType,
      "Operations.Operand", "PersistentDisciplines.Object", NIL);
END ulmOperations.
