language-Modula2-0.1: examples/Modula-2_Libraries/C.-Lins_Modula-2_Software_Component_Library/Vol4/maps/mapdsbmi.mod
(*
4.3 Discrete Map Implementation
*)
IMPLEMENTATION MODULE MapDSBMI;
(*====================================================================
Version : 2.0 16 Sep 1990 C. Lins
Compiler : Generic pc Modula-2
Component: Monolithic Structures - Map (Opaque version)
Discrete Sequential Bounded Managed Iterator
INTRODUCTION
This module provides operations for the discrete map abstract
data type.
REVISION HISTORY
v1.01a 15 May 1988 C. Lins
Initial implementation for TML Modula-2.
v2.00 16 Sep 1990 C. Lins
Created generic pc version
(C) Copyright 1990 Charles A. Lins
=====================================================================*)
FROM SYSTEM IMPORT
(*--Type*) BYTE;
FROM SCLStorage IMPORT
(*--Proc*) Allocate, Deallocate;
FROM MapTypes IMPORT
(*--Type*) Exceptions, Operations;
FROM ErrorHandling IMPORT
(*--Type*) HandlerProc,
(*--Proc*) Raise, NullHandler, ExitOnError;
FROM TypeManager IMPORT
(*--Type*) TypeID;
(*-----------------------*)
(*
4.3.1 Type Declarations
*)
TYPE Map = POINTER TO DiscreteMap;
TYPE MapItems = ARRAY Domain OF Range;
TYPE DiscreteMap = RECORD
domain : DomainType;
range : RangeType;
size : INTEGER;
extent : INTEGER;
items : MapItems;
END (*--DiscreteMap*);
(*---------------------------------*)
(*-- EXCEPTIONS --*)
VAR mapError : Exceptions;
VAR handler : ARRAY Exceptions OF HandlerProc;
PROCEDURE MapError () : Exceptions (*-- out *);
BEGIN
RETURN mapError;
END MapError;
(*-------------------------*)
PROCEDURE GetHandler ( theError : Exceptions (*-- in *))
: HandlerProc (*-- out *);
BEGIN
RETURN handler[theError];
END GetHandler;
(*-------------------------*)
PROCEDURE SetHandler ( theError : Exceptions (*-- in *);
theHandler : HandlerProc (*-- in *));
BEGIN
handler[theError] := theHandler;
END SetHandler;
(*-------------------------*)
PROCEDURE RaiseErrIn ( theRoutine : Operations (*-- in *);
theError : Exceptions (*-- in *));
BEGIN
mapError := theError;
Raise(ComponentID + ModuleID, theRoutine, theError, handler[theError]);
END RaiseErrIn;
(*-------------------------*)
(*
4.3.2 Local Routines
*)
PROCEDURE ValidDomain ( theItem : Domain (*-- in *);
theDomain : DomainType (*-- in *))
: BOOLEAN (*-- out *);
BEGIN
WITH theDomain DO
RETURN (first <= theItem) & (theItem <= last);
END (*--with*);
END ValidDomain;
(*-------------------------*)
PROCEDURE ValidRange ( theItem : Domain (*-- in *);
theRange : RangeType (*-- in *))
: BOOLEAN (*-- out *);
BEGIN
WITH theRange DO
RETURN (first <= theItem) & (theItem <= last);
END (*--with*);
END ValidRange;
(*-------------------------*)
PROCEDURE AdjustDomain ( theItem : Domain (*-- in *);
theDomain : DomainType (*-- in *))
: Item (*-- out *);
BEGIN
RETURN theItem - theDomain.first; (*-- force zero-based *)
END AdjustDomain;
(*-------------------------*)
(*---------------------------------*)
(*-- CONSTRUCTORS --*)
PROCEDURE Create ( theDomain : DomainType (*-- in *);
theRange : RangeType (*-- in *))
: Map (*-- out *);
CONST baseSize = SIZE(DiscreteMap) - SIZE(MapItems);
CONST byteSize = SIZE(Item);
VAR newMap : Map;
VAR theSize : INTEGER;
BEGIN
mapError := noerr;
IF theDomain.first > theDomain.last THEN
RaiseErrIn(create, outofdomain);
newMap := NullMap;
ELSIF theRange.first > theRange.last THEN
RaiseErrIn(create, outofrange);
newMap := NullMap;
ELSE
WITH theDomain DO
theSize := last - first + 1;
END (*--with*);
Allocate(newMap, baseSize + byteSize * theSize);
IF (newMap = NIL) THEN
RaiseErrIn(create, overflow);
ELSE
WITH newMap^ DO
domain := theDomain;
range := theRange;
size := theSize;
END (*--with*);
Clear(newMap);
END (*--if*);
END (*--if*);
RETURN newMap;
END Create;
(*-------------------------*)
PROCEDURE Destroy (VAR theMap : Map (*-- inout *));
CONST baseSize = SIZE(DiscreteMap) - SIZE(MapItems);
CONST byteSize = SIZE(Item);
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(destroy, undefined);
ELSE
Deallocate(theMap, baseSize + byteSize * theMap^.size);
END (*--if*);
END Destroy;
(*-------------------------*)
PROCEDURE Clear (VAR theMap : Map (*-- inout *));
VAR index : INTEGER; (*-- loop index over items of Domain *)
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(clear, undefined);
ELSE
WITH theMap^ DO
FOR index := MIN(Item) TO size-1 DO
items[index] := range.undefined;
END (*--for*);
extent := 0;
END (*--with*);
END (*--if*);
END Clear;
(*-------------------------*)
PROCEDURE Assign ( theMap : Map (*-- in *);
VAR toMap : Map (*-- inout *));
VAR index : INTEGER; (*-- loop index over items of Domain *)
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(assign, undefined);
ELSIF (theMap = toMap) THEN
RETURN;
ELSIF (toMap = NIL) THEN
toMap := Create(theMap^.domain, theMap^.range);
ELSE
Destroy(toMap);
toMap := Create(theMap^.domain, theMap^.range);
END (*--if*);
IF (mapError = noerr) THEN
WITH theMap^ DO
FOR index := 0 TO size-1 DO
toMap^.items[index] := items[index];
END (*--for*);
toMap^.extent := extent;
END (*--with*);
END (*--if*);
END Assign;
(*-------------------------*)
PROCEDURE Bind (VAR theMap : Map (*-- inout *);
theItemIn : Domain (*-- in *);
toItemIn : Range (*-- in *));
VAR adjustedDomain : Item; (*-- numeric equivalent of Domain *)
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(bind, undefined);
ELSIF ~ValidDomain(theItemIn, theMap^.domain) THEN
RaiseErrIn(bind, outofdomain);
ELSIF ~ValidRange(toItemIn, theMap^.range) THEN
RaiseErrIn(bind, outofrange);
ELSE
WITH theMap^ DO
adjustedDomain := AdjustDomain(theItemIn, domain);
IF (items[adjustedDomain] = range.undefined) THEN
INC(extent);
END (*--if*);
items[adjustedDomain] := toItemIn;
END (*--with*);
END (*--if*);
END Bind;
(*-------------------------*)
PROCEDURE Unbind (VAR theMap : Map (*-- inout *);
theItemIn : Domain (*-- in *));
VAR adjustedDomain : Item; (*-- numeric equivalent of Domain *)
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(unbind, undefined);
ELSIF ~ValidDomain(theItemIn, theMap^.domain) THEN
RaiseErrIn(unbind, outofdomain);
ELSE
WITH theMap^ DO
adjustedDomain := AdjustDomain(theItemIn, domain);
IF (items[adjustedDomain] # range.undefined) THEN
DEC(extent);
END (*--if*);
items[adjustedDomain] := range.undefined;
END (*--with*);
END (*--if*);
END Unbind;
(*-------------------------*)
(*---------------------------------*)
(*-- SELECTORS --*)
PROCEDURE IsDefined ( theMap : Map (*-- in *))
: BOOLEAN (*-- out *);
BEGIN
RETURN theMap # NIL;
END IsDefined;
(*-------------------------*)
PROCEDURE IsEmpty ( theMap : Map (*-- in *))
: BOOLEAN (*-- out *);
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(isempty, undefined);
RETURN TRUE;
END (*--if*);
RETURN theMap^.extent = 0;
END IsEmpty;
(*-------------------------*)
PROCEDURE IsEqual ( left : Map (*-- in *);
right : Map (*-- in *))
: BOOLEAN (*-- out *);
VAR index : INTEGER; (*-- loop index over items of Domain *)
BEGIN
mapError := noerr;
IF (left = NIL) OR (right = NIL) THEN
RaiseErrIn(isequal, undefined);
ELSIF (left^.domain.dataType # right^.domain.dataType) THEN
RaiseErrIn(isequal, typeerror);
ELSE
WITH left^ DO
FOR index := MIN(Item) TO size-1 DO
IF (items[index] # right^.items[index]) THEN
RETURN FALSE;
END (*--if*);
END (*--for*);
END (*--with*);
RETURN TRUE;
END (*--if*);
RETURN FALSE;
END IsEqual;
(*-------------------------*)
PROCEDURE ExtentOf ( theMap : Map (*-- in *))
: CARDINAL (*-- out *);
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(extentof, undefined);
RETURN 0;
END (*--if*);
RETURN theMap^.extent;
END ExtentOf;
(*-------------------------*)
PROCEDURE DomainOf ( theMap : Map (*-- in *);
VAR theDomain : DomainType (*-- out *));
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(domainof, undefined);
ELSE
theDomain := theMap^.domain;
END (*--if*);
END DomainOf;
(*-------------------------*)
PROCEDURE RangeOf ( theMap : Map (*-- in *);
VAR theRange : RangeType (*-- out *));
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(rangeof, undefined);
ELSE
theRange := theMap^.range;
END (*--if*);
END RangeOf;
(*-------------------------*)
PROCEDURE IsBound ( theMap : Map (*-- in *);
theItem : Domain (*-- in *))
: BOOLEAN (*-- out *);
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(isbound, undefined);
ELSIF ~ValidDomain(theItem, theMap^.domain) THEN
RaiseErrIn(isbound, outofdomain);
ELSE
WITH theMap^ DO
RETURN items[AdjustDomain(theItem, domain)] # range.undefined;
END (*--with*);
END (*--if*);
RETURN FALSE;
END IsBound;
(*-------------------------*)
PROCEDURE BoundTo ( theMap : Map (*-- in *);
theItem : Domain (*-- in *))
: Range (*-- out *);
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(boundto, undefined);
ELSIF ~ValidDomain(theItem, theMap^.domain) THEN
RaiseErrIn(boundto, outofdomain);
ELSE
WITH theMap^ DO
RETURN items[AdjustDomain(theItem, domain)];
END (*--with*);
END (*--if*);
RETURN theMap^.range.undefined;
END BoundTo;
(*-------------------------*)
PROCEDURE IsBoundTo ( theMap : Map (*-- in *);
theItem : Domain (*-- in *);
VAR toItem : Range (*-- out *))
: BOOLEAN (*-- out *);
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(isboundto, undefined);
ELSIF ~ValidDomain(theItem, theMap^.domain) THEN
RaiseErrIn(isboundto, outofdomain);
ELSE
WITH theMap^ DO
toItem := items[AdjustDomain(theItem, domain)];
RETURN toItem # range.undefined;
END (*--with*);
END (*--if*);
RETURN FALSE;
END IsBoundTo;
(*-------------------------*)
(*---------------------------------*)
(*-- ITERATORS --*)
PROCEDURE LoopOver ( theMap : Map (*-- in *);
theProcess: LoopAccessProc (*-- in *));
VAR index : Item; (*-- loop index over items of Domain *)
first : Item; (*-- numeric equivalent of Domain.first *)
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(loopover, undefined);
ELSE
WITH theMap^ DO
first := domain.first;
FOR index := MIN(Item) TO VAL(Item, size-1) DO
IF (items[index] # range.undefined) THEN
IF ~theProcess(VAL(Domain, index + first), items[index]) THEN
RETURN;
END (*--if*);
END (*--if*);
END (*--for*);
END (*--with*);
END (*--if*);
END LoopOver;
(*-------------------------*)
PROCEDURE LoopChange ( theMap : Map (*-- in *);
theProcess: LoopChangeProc (*-- in *));
VAR index : Item; (*-- loop index over items of Domain *)
first : Item; (*-- numeric equivalent of Domain.first *)
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(loopchange, undefined);
ELSE
WITH theMap^ DO
first := domain.first;
FOR index := MIN(Item) TO VAL(Item, size-1) DO
IF (items[index] # range.undefined) THEN
IF ~theProcess(VAL(Domain, index + first), items[index]) THEN
RETURN;
END (*--if*);
END (*--if*);
END (*--for*);
END (*--with*);
END (*--if*);
END LoopChange;
(*-------------------------*)
PROCEDURE Traverse ( theMap : Map (*-- in *);
theProcess: AccessProc (*-- in *));
VAR index : Item; (*-- loop index over items of Domain *)
first : Item; (*-- numeric equivalent of Domain.first *)
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(traverse, undefined);
ELSE
WITH theMap^ DO
first := domain.first;
FOR index := MIN(Item) TO VAL(Item, size-1) DO
IF (items[index] # range.undefined) THEN
theProcess(VAL(Domain, index + first), items[index]);
END (*--if*);
END (*--for*);
END (*--with*);
END (*--if*);
END Traverse;
(*-------------------------*)
PROCEDURE TravChange ( theMap : Map (*-- in *);
theProcess: ChangeProc (*-- in *));
VAR index : Item; (*-- loop index over items of Domain *)
first : Item; (*-- numeric equivalent of Domain.first *)
BEGIN
mapError := noerr;
IF (theMap = NIL) THEN
RaiseErrIn(travchange, undefined);
ELSE
WITH theMap^ DO
first := domain.first;
FOR index := MIN(Item) TO VAL(Item, size-1) DO
IF (items[index] # range.undefined) THEN
theProcess(VAL(Domain, index + first), items[index]);
END (*--if*);
END (*--for*);
END (*--with*);
END (*--if*);
END TravChange;
(*-------------------------*)
(*
4.3.7 Module Initialization
The module's local variables are initialized to known states.
mapError is used to fill the handlers array with a routine
that will exit the program when an exception is raised (saving the
declaration of a special loop control variable for this purpose).
The condition noerr is given the NullHandler which is presumed to
do nothing. Applying MIN and MAX to cover all exceptions followed
by resetting the handler for noerr ensures that this initialization
will be unaffected by any future changes to the number of Exceptions
or their order of declaration within the enumeration. Since a FOR loop
control variable is undefined following the loop, mapError must be
set to indicate that an error has not yet occurred.
*)
BEGIN
FOR mapError := MIN(Exceptions) TO MAX(Exceptions) DO
SetHandler(mapError, ExitOnError);
END (*--for*);
SetHandler(noerr, NullHandler);
mapError := noerr;
NullMap := NIL;
END MapDSBMI.
(*
References
*)