language-Modula2-0.1: examples/Modula-2_Libraries/C.-Lins_Modula-2_Software_Component_Library/Vol1/STRINGS/STRSUUI.MOD
IMPLEMENTATION MODULE StrSUUI;
(*==========================================================
Version : 2.00 29 Apr 1989 C. Lins
Compiler : TopSpeed Modula-2
Code Size: R- bytes
Component: Monolithic Structure String (Opaque version)
Sequential Unbounded Unmanaged Iterator
REVISION HISTORY
v2.00 29 Apr 1989 C. Lins:
Initial re-implementation from StringCSUMI module.
(C) Copyright 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, CompareProc, AccessProc, ChangeProc,
LoopAccessProc, LoopChangeProc;
FROM Relations IMPORT
(*--Type*) Relation;
FROM StrEnum IMPORT
(*--Type*) Exceptions, Operations, ComponentID;
FROM TypeManager IMPORT
(*--Cons*) NullType,
(*--Type*) TypeID,
(*--Proc*) CompareOf;
(*-----------------------*)
TYPE Substring = POINTER TO ARRAY Position OF Item;
TYPE UnboundedString = RECORD
dataID : TypeID; (*-- data type of the string *)
size : CARDINAL; (*-- 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 *)
BEGIN
setSizeOK := TRUE;
WITH theString DO
IF (totheSize = 0) THEN
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 > length) THEN
Allocate(newItems, itemSize * totheSize);
IF (newItems = NIL) THEN
setSizeOK := FALSE;
RETURN;
END (*--if*);
IF (valueOption = preservevalue) THEN
FOR index := MIN(Position) TO length DO
newItems^[index] := items^[index];
END (*--for*);
END (*--if*);
Deallocate(items, itemSize * size);
items := newItems;
size := totheSize;
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, SIZE(UnboundedString));
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 *)
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
FOR index := MIN(Position) TO length DO
toString^.items^[index] := 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 *)
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*);
FOR index := MIN(Position) TO theString^.length DO
items^[theIndex + index - 1] := 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 *)
BEGIN
stringError := noerr;
IF (theString # NIL) THEN
WITH theString^ DO
IF FromToOK(fromIndex, toIndex, length) THEN
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 *)
BEGIN
stringError := noerr;
IF (theString # NIL) & (withString # NIL) THEN
WITH theString^ DO
IF (theIndex <= length) THEN
endIndex := theIndex + withString^.length - 1;
IF (endIndex > length) THEN
SetSize(theString^, endIndex, preservevalue);
IF ~setSizeOK THEN
RaiseErrIn(replace, overflow);
RETURN;
END (*--if*);
END (*--if*);
FOR index := MIN(Position) TO withString^.length DO
items^[theIndex + index - 1] := 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 *));
BEGIN
stringError := noerr;
IF (theString # NIL) THEN
WITH theString^ DO
IF (theIndex <= length) THEN
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 StrSUUI.