packages feed

language-Modula2-0.1: examples/Modula-2_Libraries/C.-Lins_Modula-2_Software_Component_Library/Vol1/STRINGS/STRSUMI.MOD

IMPLEMENTATION MODULE StringSUMI; 
(*==========================================================
    Version  : 1.00  21 May 1989  C. Lins
    Compiler : TopSpeed Modula-2 Compiler
    Code Size: R-  bytes
    Component: Monolithic Structure String (Opaque version)
	Sequential Unbounded Managed Iterator

	The major change necessary was the freeing items of
	a string. This effected the following routines:
	* SetSize
		All items must be freed when the string is cleared or
		when the string size is increased and the value is not
		preserved.
		When the string length is decreased, any items beyond
		the end of the new string length must be freed.
	* Replace
		Must free the items being overwritten.
	* SetItem
		Must free the single item being overwritten.
	
	Secondarily, the assignment operation derived from the
	string's data type id must be used instead of simple
	assignment. Simple assignment (:=) is still used internally
	by Insert and Delete to move items within the string.
	
	For the JPI version we must keep track of the physically
	allocated size of the array for when we deallocate the array.
	
    REVISION HISTORY
    v1.00  21 May 1989  C. Lins:
	Initial re-implementation from StringCSUMI module.

    c 1989 Charles A. Lins.
==========================================================*)

FROM JPIStorage IMPORT
    (*--Proc*) Allocate, Deallocate;

FROM ErrorHandling IMPORT
    (*--Type*) HandlerProc,
    (*--Proc*) NullHandler, ExitOnError, Raise;

FROM Items IMPORT
    (*--Cons*) NullItem,
    (*--Type*) Item, AssignProc, ChangeProc, CompareProc,
			   AccessProc, LoopAccessProc, LoopChangeProc,
			   DisposeProc;

FROM Relations IMPORT
    (*--Type*) Relation;

FROM StringEnum IMPORT
    (*--Type*) Exceptions, Operations, ComponentID;

FROM TypeManager IMPORT
	(*--Cons*) NullType,
    (*--Type*) TypeID,
	(*--Proc*) AssignOf, CompareOf, DisposeOf;

    (*-----------------------*)


TYPE  Substring  = POINTER TO ARRAY Position OF Item;

TYPE  UnboundedString = RECORD
		dataID : TypeID;	  (*-- data type of the string *)
		size   : CARDINAL;	  (*-- actual allocated size *)
	length : CARDINAL;    (*-- Current String Length *)
	items  : Substring;   (*-- ARRAY[1..length] of Items *)
      END (*-- UnboundedString *);

TYPE  String = POINTER TO UnboundedString;


    (*-----------------------*)


VAR   stringError : Exceptions;
VAR   handler     : ARRAY Exceptions OF HandlerProc;

    (*-----------------------*)

PROCEDURE StringError ()	: Exceptions  (*-- out   *);
BEGIN
  RETURN stringError;
END StringError;
(*----------------------------*)

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
  stringError := theError;
  Raise(ComponentID + ModuleID, theRoutine, theError, handler[theError]);
END RaiseErrIn;
(*----------------------------*)


    (*-----------------------*)

PROCEDURE LengthSubstr  (VAR theSubstring : ARRAY OF Item (*-- in    *))
	: CARDINAL      (*-- out   *);
BEGIN
  IF (HIGH(theSubstring) = 0) & (theSubstring[0] = NullItem) THEN
    RETURN 0;
  END (*--if*);
  RETURN HIGH(theSubstring) + 1;
END LengthSubstr;
(*----------------------------*)

PROCEDURE FromToOK      (    fromIndex    : Position   (*-- in    *);
	toIndex      : Position   (*-- in    *);
	stringLength : CARDINAL   (*-- in    *))
	: BOOLEAN    (*-- out   *);
BEGIN
  RETURN (toIndex <= stringLength) & (fromIndex <= toIndex);
END FromToOK;
(*----------------------------*)


TYPE  SetOptions = (preservevalue, trashvalue);
VAR   setSizeOK  : BOOLEAN;

PROCEDURE SetSize   (VAR theString   : UnboundedString (*-- inout *);
	totheSize   : CARDINAL	(*-- in    *);
	valueOption : SetOptions      (*-- in    *));

CONST itemSize = SIZE(Item);
VAR   newItems : Substring;
      index    : CARDINAL; (*-- loop index over items *)
	  free	   : DisposeProc;
	  assign   : AssignProc;

BEGIN
  setSizeOK := TRUE;
  WITH theString DO
    IF (totheSize = 0) THEN
	  free := DisposeOf(dataID);
	  FOR index := MIN(Position) TO length DO
	  	free(items^[index]);
	  END (*--for*);
      Deallocate(items, itemSize * size);
	  size := 0;
    ELSIF (items = NIL) THEN
      Allocate(items, itemSize * totheSize);
      IF (items = NIL) THEN
	setSizeOK := FALSE;
	RETURN;
      END (*--if*);
	  size := totheSize;

    ELSIF (totheSize > size) THEN
      Allocate(newItems, itemSize * totheSize);
      IF (newItems = NIL) THEN
	setSizeOK := FALSE;
	RETURN;
      END (*--if*);

      IF (valueOption = preservevalue) THEN
	  	assign := AssignOf(dataID);
	FOR index := MIN(Position) TO length DO
	newItems^[index] := assign(items^[index]);
	END (*--for*);
	  ELSE
	  	free := DisposeOf(dataID);
	  	FOR index := MIN(Position) TO length DO
	  	  free(items^[index]);
	  	END (*--for*);
      END (*--if*);

      Deallocate(items, itemSize * size);
	  size  := totheSize;
      items := newItems;
	ELSIF (valueOption = trashvalue) THEN
	  free := DisposeOf(dataID);
	  FOR index := totheSize+1 TO length DO
	  	free(items^[index]);
	  END (*--for*);
    END (*--if*);
    length := totheSize;
  END (*--with*);
END SetSize;
(*----------------------------*)


    (*-----------------------*)

PROCEDURE Create    (    theDataType: TypeID     (*-- in    *)) 
									: String     (*-- out   *);

VAR   newString : String;

BEGIN
  stringError := noerr;
  Allocate(newString, SIZE(UnboundedString));
  IF (newString # NIL) THEN
    WITH newString^ DO
	  dataID := theDataType;
	  size   := 0;
      length := 0;
      items  := NIL;
    END (*--with*);
    RETURN newString;
  END (*--if*);
  RaiseErrIn(create, overflow);
  RETURN NullString;
END Create;
(*----------------------------*)


PROCEDURE Destroy   (VAR theString  : String     (*-- inout *));
BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    SetSize(theString^, 0, trashvalue);
    Deallocate(theString);
  ELSE
    RaiseErrIn(destroy, undefined);
  END (*--if*);
END Destroy;
(*----------------------------*)


PROCEDURE Clear     (VAR theString  : String     (*-- inout *));
BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    SetSize(theString^, 0, trashvalue);
  ELSE
    RaiseErrIn(clear, undefined);
  END (*--if*);
END Clear;
(*----------------------------*)


PROCEDURE Assign    (    theString  : String     (*-- in    *);
	VAR toString   : String     (*-- inout *));

VAR   index : CARDINAL; (*-- loop index over items *)
	  assignment : AssignProc;

BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    IF (theString # toString) THEN
      IF (toString # NIL) THEN
	SetSize(toString^, theString^.length, trashvalue);
	IF ~setSizeOK THEN
	RaiseErrIn(assign, overflow);
	RETURN;
	END (*--if*);
      ELSE
	toString := Create(theString^.dataID);
	IF (stringError # noerr) THEN
	RETURN;
	END (*--if*);
      END (*--if*);

      WITH theString^ DO
	  	assignment := AssignOf(dataID);
	FOR index := MIN(Position) TO length DO
	toString^.items^[index] := assignment(items^[index]);
	END (*--for*);
      END (*--with*);
    END (*--if*);
  ELSE
    RaiseErrIn(assign, undefined);
  END (*--if*);
END Assign;
(*----------------------------*)


PROCEDURE Prepend   (    theString  : String     (*-- in    *);
	VAR toString   : String     (*-- inout *));
BEGIN
  Insert(theString, toString, 1);
END Prepend;
(*----------------------------*)

PROCEDURE Append    (    theString  : String     (*-- in    *);
	VAR toString   : String     (*-- inout *));
BEGIN
  Insert(theString, toString, LengthOf(toString) + 1);
END Append;
(*----------------------------*)


PROCEDURE Insert    (    theString  : String     (*-- in    *);
	VAR toString   : String     (*-- inout *);
	theIndex   : Position   (*-- in    *));

VAR   oldLength : CARDINAL;
      newLength : CARDINAL;
      index     : CARDINAL; (*-- loop index over items *)
	  assignment: AssignProc;

BEGIN
  stringError := noerr;
  IF (toString # NIL) & (theString # NIL) THEN

    WITH toString^ DO
      oldLength := length;
      newLength := theString^.length + length;

      IF (theIndex > oldLength + 1) THEN
	RaiseErrIn(insert, positionerr);
      ELSE
	SetSize(toString^, newLength, preservevalue);
	IF setSizeOK THEN

	FOR index := oldLength TO theIndex BY -1 DO
	items^[index + theString^.length] := items^[index];
	END (*--for*);

	  	  assignment := AssignOf(dataID);
	FOR index := MIN(Position) TO theString^.length DO
	items^[theIndex + index - 1] := assignment(theString^.items^[index]);
	END (*--for*);
	ELSE
	RaiseErrIn(insert, overflow);
	END (*--if*);
      END (*--if*);
    END (*--with*);
  ELSE
    RaiseErrIn(insert, undefined);
  END (*--if*);
END Insert;
(*----------------------------*)


PROCEDURE Delete    (VAR theString  : String     (*-- inout *);
	fromIndex  : Position   (*-- in    *);
	toIndex    : Position   (*-- in    *));

VAR   index  : CARDINAL; (*-- loop index over items *)
      offset : CARDINAL; (*-- distance to move items *)
	  free   : DisposeProc;

BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    WITH theString^ DO
      IF FromToOK(fromIndex, toIndex, length) THEN

	  	free := DisposeOf(dataID);
		FOR index := fromIndex TO toIndex DO
		  free(items^[index]);
		END (*--for*);

	offset := toIndex - fromIndex + 1;
	FOR index := toIndex + 1 TO length DO
	items^[index - offset] := items^[index];
	END (*--for*);
	SetSize(theString^, length - offset, preservevalue);
      ELSE
	RaiseErrIn(delete, positionerr);
      END (*--if*);
    END (*--with*);
  ELSE
    RaiseErrIn(delete, undefined);
  END (*--if*);
END Delete;
(*----------------------------*)


PROCEDURE Replace   (VAR theString  : String     (*-- inout *);
	theIndex   : Position   (*-- in    *);
	withString : String     (*-- in    *));

VAR   endIndex   : CARDINAL;
      index      : CARDINAL; (*-- loop index over items *)
	  oldLength  : CARDINAL;
	  assignment : AssignProc;
	  free       : DisposeProc;

BEGIN
  stringError := noerr;
  IF (theString # NIL) & (withString # NIL) THEN
    WITH theString^ DO
      IF (theIndex <= length) THEN

		oldLength := length;
	endIndex  := theIndex + withString^.length - 1;
	IF (endIndex > length) THEN
	SetSize(theString^, endIndex, preservevalue);
	IF ~setSizeOK THEN
	RaiseErrIn(replace, overflow);
	RETURN;
	END (*--if*);
	END (*--if*);

		(*
		  -- Free the items about to be overwritten. We do this after
		  -- increasing the string's size, since if that fails we won't
		  -- damage the original string. Note that there's always at
		  -- least one item being freed as Replace cannot be used as
		  -- an append operation.
		*)
	  	free := DisposeOf(dataID);
		FOR index := theIndex TO oldLength DO
		  free(items^[index]);
		END (*--for*);
		
	  	assignment := AssignOf(dataID);
	FOR index := MIN(Position) TO withString^.length DO
	items^[theIndex + index - 1] := assignment(withString^.items^[index]);
	END (*--for*);
      ELSE
	RaiseErrIn(replace, positionerr);
      END (*--if*);
    END (*--with*);
  ELSE
    RaiseErrIn(replace, undefined);
  END (*--if*);
END Replace;
(*----------------------------*)


PROCEDURE SetItem   (VAR theString  : String     (*-- inout *);
	theIndex   : Position   (*-- in    *);
	theItem    : Item       (*-- in    *));

VAR	  free : DisposeProc;

BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    WITH theString^ DO
      IF (theIndex <= length) THEN
	  	free := DisposeOf(dataID);
		free(items^[theIndex]);
	items^[theIndex] := theItem;
      ELSE
	RaiseErrIn(setitem, positionerr);
      END (*--if*);
    END (*--with*);
  ELSE
    RaiseErrIn(setitem, undefined);
  END (*--if*);
END SetItem;
(*----------------------------*)


PROCEDURE Construct (VAR theString   : String	(*-- inout *);
						 theDataType : TypeID		 (*-- in    *);
	theSubstring: ARRAY OF Item (*-- in    *));

VAR   index     : CARDINAL; (*-- loop index over items *)
      lenSubstr : CARDINAL; (*-- # of items in substring *)
      newString : String;   (*-- new string, if necessary *)

BEGIN
  stringError := noerr;
  lenSubstr := LengthSubstr(theSubstring);

  IF (theString # NIL) THEN
    IF (lenSubstr = 0) THEN
      Clear(theString);
      RETURN;
    END (*--if*);
  ELSIF (lenSubstr = 0) THEN
    RaiseErrIn(construct, positionerr);
  ELSE
    newString := Create(theDataType);
    IF (stringError # noerr) THEN
      RETURN;
    END (*--if*);
    theString := newString;
  END (*--if*);

  SetSize(theString^, lenSubstr, trashvalue);
  IF setSizeOK THEN
    WITH theString^ DO
      index := MIN(Position);
      WHILE (index <= lenSubstr) &
	(theSubstring[index - 1] # NullItem) DO
	items^[index] := theSubstring[index - 1];
	INC(index);
      END (*--while*);
    END (*--with*);
	SetSize(theString^, index-1, preservevalue);
  ELSE
    RaiseErrIn(construct, overflow);
  END (*--if*);
END Construct;
(*----------------------------*)


PROCEDURE IsDefined  (    theString : String   (*-- in    *))
	: BOOLEAN  (*-- out   *);
BEGIN
  RETURN (theString # NIL);
END IsDefined;
(*----------------------------*)

PROCEDURE IsEmpty    (    theString : String   (*-- in    *))
	: BOOLEAN  (*-- out   *);
BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    RETURN theString^.length = 0;
  END (*--if*);
  RaiseErrIn(isempty, undefined);
  RETURN TRUE;
END IsEmpty;
(*----------------------------*)

PROCEDURE LengthOf   (    theString : String   (*-- in    *))
	: CARDINAL (*-- out   *);
BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    RETURN theString^.length;
  END (*--if*);
  RaiseErrIn(lengthof, undefined);
  RETURN 0;
END LengthOf;
(*----------------------------*)

PROCEDURE TypeOf     (    theString : String   (*-- in    *))
	: TypeID   (*-- out   *);
BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    RETURN theString^.dataID;
  END (*--if*);
  RaiseErrIn(typeof, undefined);
  RETURN NullType;
END TypeOf;
(*----------------------------*)

PROCEDURE Compare    (    left      : String   (*-- in    *);
	right     : String   (*-- in    *))
	: Relation (*-- out   *);

VAR   index     : CARDINAL; (*-- Index into items arrays *)
      minLength : CARDINAL; (*-- Smaller of the two string lengths *)
      relOrder  : Relation; (*-- Most recent comparison result *)
	  comparison: CompareProc;

BEGIN
  stringError := noerr;
  relOrder    := incomparable;
  IF (left # NIL) & (right # NIL) THEN

	IF (left^.dataID = right^.dataID) THEN
      WITH left^ DO
	    comparison := CompareOf(dataID);
	IF (length = right^.length) THEN
	relOrder  := equal;
	minLength := length;
	ELSIF (length < right^.length) THEN
	relOrder  := less;
	minLength := length;
	ELSE
	relOrder  := greater;
	minLength := right^.length;
	END (*--if*);
      END (*--with*);

      index := MIN(Position);
      LOOP
	IF (index > minLength) THEN
	EXIT (*--loop*);
	END (*--if*);

	IF (comparison(left^.items^[index], right^.items^[index]) = less) THEN
	relOrder := less;
	EXIT (*--loop*);
	ELSIF (comparison(left^.items^[index], right^.items^[index]) = greater) THEN
	relOrder := greater;
	EXIT (*--loop*);
	END (*--if*);

	INC(index);

      END (*--loop*);
    ELSE
  	  RaiseErrIn(compare, typeerror);
    END (*--if*);
  ELSE
    RaiseErrIn(compare, undefined);
  END (*--if*);

  RETURN relOrder;
END Compare;
(*----------------------------*)


PROCEDURE IsEqual    (    left      : String   (*-- in    *);
	right     : String   (*-- in    *))
	: BOOLEAN  (*-- out   *);
BEGIN
  RETURN Compare(left, right) = equal;
END IsEqual;
(*----------------------------*)

PROCEDURE ItemOf     (    theString : String   (*-- in    *);
	theIndex  : Position (*-- in    *))
	: Item     (*-- out   *);
BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    WITH theString^ DO
      IF (theIndex <= length) THEN
	RETURN items^[theIndex];
      END (*--if*);
    END (*--with*);
    RaiseErrIn(itemof, positionerr);
  ELSE
    RaiseErrIn(itemof, undefined);
  END (*--if*);
  RETURN NullItem;
END ItemOf;
(*----------------------------*)

PROCEDURE SliceOf   (    theString  : String	(*-- in    *);
	fromIndex  : Position      (*-- in    *);
	toIndex    : Position      (*-- in    *);
	VAR theSlice   : ARRAY OF Item (*-- out   *));

VAR   index     : CARDINAL; (*-- loop index over items *)
      sliceSize : CARDINAL; (*-- # items between from & to indexes *)

BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    WITH theString^ DO
      IF FromToOK(fromIndex, toIndex, length) THEN
	sliceSize := toIndex - fromIndex;
	IF (sliceSize <= HIGH(theSlice)) THEN
	FOR index := fromIndex TO toIndex DO
	theSlice[index - fromIndex] := items^[index];
	END (*--for*);
	IF (sliceSize < HIGH(theSlice)) THEN
	theSlice[sliceSize + 1] := NullItem;
	END (*--if*);
	ELSE
	RaiseErrIn(sliceof, overflow);
	END (*--if*);
      ELSE
	RaiseErrIn(sliceof, positionerr);
      END (*--if*);
    END (*--with*);
  ELSE
    RaiseErrIn(sliceof, undefined);
  END (*--if*);
END SliceOf;
(*----------------------------*)


PROCEDURE SubstringOf(    theString  : String	(*-- in    *);
	VAR toSubstring: ARRAY OF Item (*-- out   *));

VAR   index      : CARDINAL; (*-- loop index over items *)
      copyLength : CARDINAL; (*-- # items to copy into substring *)

BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    WITH theString^ DO
      IF (length > HIGH(toSubstring) + 1) THEN
	RaiseErrIn(substringof, overflow);
	copyLength := HIGH(toSubstring) + 1;
      ELSE
	copyLength := length;
      END (*--if*);
      FOR index := MIN(Position) TO copyLength DO
	toSubstring[index - 1] := items^[index];
      END (*--for*);
      IF copyLength < HIGH(toSubstring) THEN
	toSubstring[copyLength + 1] := NullItem;
      END (*--if*);
    END (*--with*);
  ELSE
    RaiseErrIn(substringof, undefined);
  END (*--if*);
END SubstringOf;
(*----------------------------*)


PROCEDURE LoopOver   (    theString : String	(*-- in    *);
	theProcess: LoopAccessProc (*-- in    *));

VAR   index : CARDINAL; (*-- loop index over items *)

BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    WITH theString^ DO
      FOR index := MIN(Position) TO length DO
	IF ~theProcess(items^[index]) THEN
	RETURN;
	END (*--if*);
      END (*--for*);
    END (*--with*);
  ELSE
    RaiseErrIn(loopover, undefined);
  END (*--if*);
END LoopOver;
(*----------------------------*)

PROCEDURE LoopChange (    theString : String	(*-- in    *);
	theProcess: LoopChangeProc (*-- in    *));

VAR   index : CARDINAL; (*-- loop index over items *)

BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    WITH theString^ DO
      FOR index := MIN(Position) TO length DO
	IF ~theProcess(items^[index]) THEN
	RETURN;
	END (*--if*);
      END (*--for*);
    END (*--with*);
  ELSE
    RaiseErrIn(loopchange, undefined);
  END (*--if*);
END LoopChange;
(*----------------------------*)


PROCEDURE Traverse   (    theString : String	(*-- in    *);
	theProcess: AccessProc     (*-- in    *));

VAR   index : CARDINAL; (*-- loop index over items *)

BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    WITH theString^ DO
      FOR index := MIN(Position) TO length DO
	theProcess(items^[index]);
      END (*--for*);
    END (*--with*);
  ELSE
    RaiseErrIn(traverse, undefined);
  END (*--if*);
END Traverse;
(*----------------------------*)


PROCEDURE TravChange (    theString : String	(*-- in    *);
	theProcess: ChangeProc     (*-- in    *));

VAR   index : CARDINAL; (*-- loop index over items *)

BEGIN
  stringError := noerr;
  IF (theString # NIL) THEN
    WITH theString^ DO
      FOR index := MIN(Position) TO length DO
	theProcess(items^[index]);
      END (*--for*);
    END (*--with*);
  ELSE
    RaiseErrIn(travchange, undefined);
  END (*--if*);
END TravChange;
(*----------------------------*)


BEGIN
  FOR stringError := MIN(Exceptions) TO MAX(Exceptions) DO
    SetHandler(stringError, ExitOnError);
  END (*--for*);
  SetHandler(noerr, NullHandler);
  stringError := noerr;
END StringSUMI.